javascript零碎总结

前言:下面的总结都是自己测试过的,绝大多数都是没问题的,但是不敢保证一定全面,还需要诸君自己独立思考;

1)基础:在nodejs里全局对象是global,而网页里js的全局对象是window;对于所有在全局范围内声明的var或非var变量如obj,都可以用global.obj/window.obj来引用;

对于全局范围里的变量用var和不用var是一样的,但是对于方法内的变量则var能够限定变量的scope只存在于该方法内,而非var的变量则仍然属于全局对象;(上述定义对于方法的归属和scope也是一样的)

2)对于1)的补充:如function test(){pro=33;};那么pro确实是属于window的,可以window.pro来引用,但是前提是先执行了一次test();

3)重要:如果有function test(){...;return result;};那么new test()产生的对象是根据result的类型来决定的,如果result是null/String/Boolean/Number这些基础的类型,则new test()获得的就是test类对象,如果result是复杂类型则new test()返回的是result;

4)重要:如果存在function test(){this.pro=33};但是却是以普通方法执行的如test();那么pro将会是全局对象的属性,因为test();等价于window.test();,如果是mm.test();则test方法内的this就是mm方法对象/类;

5)基础:对于function test(arg0){..};在调用test方法时是可以不指定形参值的如test();,此时在test方法内部arg0是一个undefined变量;但一般都用if(typeof arg0 === "undefined")来判断;

6)基础:对于function test(){...};虽然test没有声明参数列表,但是调用test方法时还是可以提供参数的如test(33,44);而test内部可以通过arguments来获取参数,如arguments[0]就是33;

arguments[0]就像shell里的$1;也像java里的test(Object ... arguments){}或C#里的test(params Object arguments){}

7)基础:==和===的区别最大在于判断时还包括对类型的判断,如'33'==33为true而'33'===33为false;对于!=和!==也一样;

8)语法:可以用(function (arg0){})(33);在声明匿名方法后立刻执行此方法,而前面加上new关键字则是在声明此匿名类后立刻创建一个此类对象,有点像单例模式;

9)基础:js里也是所有的类都是Object的子类,prototype就是Object类的属性;js里也提供三目运算符和逗号运算符,它们和C++的用法一样;

10)基础:var a;那么a是undefined而非像java里的成员一样有默认值如null/0/false之类的;,另外undefined是只某个变量未初始化或声明(其实就是未初始化,因为直接用也是一种声明),而null是初始化了但是值为null;

11)基础:js提供void运算符,如var a, b, c;a = (b = 5, c = 8);那么a的值是8;而如果后面改成a = void(b = 5, c = 8);则a会是undefined,而且这个undefined就是void运算符返回的,即便a原来有值也会变为undefined;不能直接void或void();

12)基础:假设testa方法定义里会调用testb方法,testb方法是可以在testa方法后面定义的,但是调有testa方法时testb必须已经定义好;

13)其它:js里可以通过“静态”方法实现java里的包路径的:

  function me(){}
    me.silentdoer = function(){}
    me.silentdoer.common = function(){}
    me.silentdoer.common.TestTool = function () {}
    me.silentdoer.common.TestTool.print = function(msg){
        if(msg != undefined){
            alert(msg);
        }
    }
    window.me.silentdoer.common.TestTool.print("UUUUU");
来实现包名管理,注意不能直接me.silentdoer.common.TestTool.print = xxx;
如果用的时候不想每次输入这么长的“包名”,可以var TestTool = me.silentdoer.common.TestTool;(类似import)
然后就可以直接TestTool.print("sss");

14)基础:js里变量有var和无var的区别之一如:var a;那么不管前面是否有a变量这里都重新声明a变量且未初始化;而直接a则如果前面有a变量这里相当于是直接引用,因此a的值是老值,但是如果前面没有a变量则也是相当于声明了一个a变量为未初始化状态;

15)其它:js里可以a = undefined;但自己目前不清楚这样做的特殊用处是什么;

16)重要:有如下方法定义

var me = function (){
        function test() {
            alert("uuuu");
        }
        test();
    }
me();
me方法内部的test方法定义就像var test = function(){...}
即function aaa(){}是局部变量的范畴,因此不能window.test();(注:如果test内部用了this由于调用test()时无法指定它归属于哪个对象,则this默认是window对象,但不是说test就是window的实例方法了,这是一个比较难解释和理解的存在,不知道有没有人有较好的理解)
,每执行me()一次都会产生一次test方法定义?;
注意test不是me的“静态方法”,如果要定义“静态方法”可以me.test = function(){..} 

17)重要:对于typeof result返回的值有:1.'object';2.'undefined';3.'function';4.'string';5.'number';6.'boolean';如果result是null/JSON对象或数组/new的对象(包括String等)都是"object",而如果result是字符串字面值则是"string",如果是方法或类则是"function"

18)基础:在js里除了数值能充当true/false,对于undefined和null同样可以;0和undefined和null都在if条件判断里都可以充当false;

19)基础:var a=8, b=9;那么a和b都是var的而非只有a是var声明的;

20)基础:对于类test的“实例属性”可以在prototype里定义,而“静态属性”则可以直接test.pro来定义;虽然test.prototype.pro=33;需要new test()才能调有pro(多实例不共享),但是也可以直接test.prototype.pro来调用(共享),prototype是一个特殊的存在;

21)基础:所有的“类”和对象都可以理解为JSON对象,因此test.prototype.pro = 33;的prototype是JSON对象,而这句代码就是给这个JSON对象添加新的属性pro值为33;因此可以test.prototype = {...}来一次性用新的JSON对象覆盖prototype的值;

22)其它:js里有length和constructor这两个实例属性比较特殊一点,js里所有的对象都可以是数组如:obj.length=1;obj[0]=33;,obj可以事先是任意对象;而constructor一般赋值为对应的类名/方法名;

23)基础:对于var uu = ss || {};,如果ss成立,则uu由ss赋值;js里的if判断可以和java的理解不同,即不理解为true/false而是理解为成立与不成立,true/非0/非undefined/非null的都成立,返回的值也是它们本来的值而不一定是true/false;

24)基础:{}和null是不一样的,{}有点像new Object();?

25)重要:js里可以对所有对象进行遍历

// js里所有对象都是JSON对象
var name, str = "";  // name会遍历JSON对象的所有属性名
    for(name in ss){
        str += name;  // 可以通过ss[name]来引用name属性的值;
    }
对JSON对象属性值的访问除了json.pro还可以json["pro"]
Test["mm"] = function(){}可以为Test设置静态方法mm,而不是必须Test.mm=function(){}
注意:比如ss是JSON对象,然后ss[0]=88;那么上面的for里既会取出属性,也会取出数组元素;js里有很多理解跟编译型语言都很大的不同有点随意但很多时候却也很好用;

26)语法:js里对象的杂合性

var test = [1,2];
    test["bb"] = "ssss";
    alert(test[0] + test["bb"]);

27)其它:js里创建对象的较好方式以及length的使用

function Test() {
        if(typeof Test._initialized === "undefined"){
            Test.prototype = {length:3,constructor:Test};
            Test._initialized = true;
        }
    }
    var test = new Test();
    test[2] = 898;
    alert(test[2]);  // length可以用来表示此对象是一个数组,虽然其实不用length或length值任意也行;

28)基础:js遍历数组

var test = [1,2];
    for(ss in test){  // ss是下标字符串值(如果是杂合对象则ss还包括是属性名)
        alert(test[ss]);  // test["0"],也可以主动用test[0]
        //break;
    }
而如果 var str = "abcd";那么str[0]是字符串a;

29)基础:js里对于方法定义的{}后面可以不加;,当是其它的最好加上;

30)基础:js里的JSON.stringify(obj)是将obj转换为JSON字符串的方法,而ify是英文的后缀,表示使..化,使..成为的意思,因此stringify是说使..成为字符串的意思(就类似静态的toString方法)

31)其它:如果用XMLHttpRequest去send时发现回调函数的status总是0可以看看自己是不是跨域访问了,注意这个跨域包括访问的url的域名/ip/port存在一个不同就是跨域,而且你的当前页面处于localhost,当是open的url却是127.0.0.1尽管它们最终是一样的浏览器也认为是跨域;

32)基础:有如下代码

var Test = function() {
                this.a = 33;
                this.foo = () => {
      // 这里注意,哪怕foo是Test的“实例”方法成员,但是如果不是this.a而是直接a,那么这里会直接提示a是undefined,这就是js特别繁琐的地方,无法省略this.
      // 这里alert(a)其实就像调用alert方法一样本质上调用的是window.alert,除非显示加上this.浏览器才能知道原来a是t.foo()的t.a
      alert(this.a);
    }
  }
var t = new Test();
t.foo();

33)js里用'mmm'括起字符串,而用/mmm/括起正则表达式的pattern,还可以在最后加个g表示全局搜索匹配(不限于单行单个,或者说返回全局匹配到的所有匹配项的list,而不加g则只返回第一个匹配的,写法是/mmm/g,还可以写/mm.*uu/g之类的)

34)如果调用JSON.stringify(data)报错:SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data,可以试着直接用data,比如console.log(data),而不需要再做转换操作;

35)js里如果要判断一个变量值不是null,不是undefined和不是NaN,可以直接用if (!obj)来判断,而不用自己写三个&&判断,如果要增加额外判断如不是-1,可以if (!obj && obj !== -1)来判断;

注意,上面的判断确实可以一次性判断变量是否是null,undefined和NaN(0/0),但是同时变量是false或0,那么(!obj)也是返回true的,所以还是自己写个专门的工具类来判断把(Object的工具方法里貌似没看到)

36)对于==和===的区别是1 == true结果是true,1 == '1'结果是true,但是'true' == true却是false(坑,不一致),用eval('true') == true是true;

而用===则是恒等比较(包括类型/原型)1 === true结果就变成了false,1==='1'结果也是false;不过eval('true') === true结果还是true,因为eval('true')就是将true字符串变成boolean类型的值;

37)对于async方法,比如async function bb() {...return 'sss'},bb方法里返回的是普通对象,但是调用bb方法时如果不await,则它的返回值会自动被封装成Promise对象,即调用bb()方法是异步调用,只不过bb()内部的await fetch(...)在bb方法内部是同步的,但是如果要await bb(),则要求该上下文的方法上也有async;

所以如果一些方法,比如forEach(async () => {return await fetch(...)}),由于forEach内部调用这个匿名方法时不会用到它的返回值,所以是没有问题的,但是假设是原来是map(a => a.itm)这样的方法定义,map方法内部会用到传入的匿名方法,将元素转换为a.itm,这个时候如果传入的是map(async a => a.itm)则会出现问题,因为map不是async,也不会去await调用匿名方法,所以这个时候a.itm会变成Promise<..>对象,这就有问题了;

没有解决办法,如果老框架不支持Promise,那么lambda表达式就不能加async,但是如果内部的数据来源由是通过fetch这样的异步方法获得的,就会出现问题(根本原因在于Promise对象同步只能靠await,而await又要求其所在上下文方法必须是async,老框架里没有支持,那么就有兼容问题,这个情况只能不用fetch改用如ajax或axios之类的本身就是同步的方法来执行;JS的Promise真垃圾,向C# 里的Task除了可以await同步获得返回值,还能task.get()来同步获得返回值,但是在JS里就只能是await,而await又必须async,真尼玛垃圾,也不提供一个如Promise.wait(promise)来同步获得值的方式)

而且注意async方法内部哪怕没有return值,js也会默认返回一个Promise对象,只不过就类似await 该对象最终得到的可能是null或undefiend而已;

38)在js领域分为前端和后端,在前端里,可以看成每个.html都拥有一个window对象(除非通过js将另一个html文件作为本html的元素嵌进来,类似动态添加dom元素一样),一个window对象只有一个document对象,它们其实可以合并为一个对象,只不过在作用领域进行了划分所以分成了两个对象,document是window的一个属性;而在后端里有个global对象,它类似window对象;每个js应用都只有一个global对象(类似每个html页面只有一个window对象,一个html里可能导入多个js文件,但是这些js文件共用一个window对象);注意iframe里面的页面和其外部页面是不共用一个window对象的,如果外部页面要获得内部iframe页面的window对象可以这么做:

var iframe=document.getElementById("xxIframe");  // 父窗口获取iframe子窗口对象
var iframew=iframe.contentWindow;  // iframe窗口的window对象
var iframed=iframew.document;  // iframe窗口的document对象
var iframed2=iframe.contentDocument;  // DOM2也支持直接获取document对象

 39)js里可以将一个JSON对象拆开来赋值给另一些变量(有点像将元组赋值给多个变量),比如let a = {"aaUm": true, "bbKs": "sfjl", "tt": false},那么我在另一个地方希望将a的属性分别赋值给多个变量(也可以只取其中一部分)

,写法为let {aaUm, bbKs, km} = a;注意,这里的变量名必须和a的属性名完全一致(包括大小写),否则无法将a的属性值赋值给对应变量(没有赋值的变量会变成undefined),这里赋值后aaUm变量的值是true,km是undefined(因为没有匹配的a对象属性)

bbKs是"sfjl"

40)export还可以直接将另一个文件的export直接在本文件里导出,如在aa.js(是es6)里可以export {ref} from './uu.js',那么在uu.js里export的ref会赋值给aa.js里的ref变量,然后又在aa.js里导出出去,外部就可以直接import {ref} from './aa.js'来导入ref而不用非得从uu.js里导入;

41.js里还有console.error(..),console.dir(..),console.dirxml(..)等方法,.dir适用于显示一个object的所有属性方法等,而.dirxml则是适合显示一个dom对象(比如Document.getElementById(..)获得的)【要用浏览器的REPL来打印才看的出区别】

42.js里最好和Python一样,对于类属性都赋予一个初始值来设定其类型,否则这样子写很怪:

class Student {
    name
    gender
}
上面的写法很奇怪(注意如果写成var name或let name会报错,因为这是不允许的)
所以最好是写成:
class Student {
    name = ''
    gender = 0
room = null } 这样赋予一个默认值/零值就和Python类似,看着就没那么怪了 或者如果以后允许写类型了,还可以 class Student { name: string } 这样写即便没有写默认值看着也不会那么难受

43.在ES6以前是没有class的,但是那个时候其实就是通过一个function来实现构造方法,new这个function就是创建对象,这种写法可以抛弃了;不过要它声明属性的方式不要觉得奇怪,因为C++,java等等的属性/字段声明前面都不会有var之类的,var只是声明全局或局部变量而不是用于声明属性/字段;

44.原始类型number(整型和浮点型)是有对应的包装类Number的,可以用let n = new Number()来创建一个引用类型的数值,n拥有原型属性__proto__(类似java里的Number.class)

45.js里面prototype是类的概念,如Student.prototype,而__proto__则是对象里的,如stud.__proto__,这两个指向的是同一个,你可以理解为类似java的Student.class对应js的Student.prototype,而stud.getClass()对应stud.__proto__

46.typeof(stud)是无法获取stud的实际类型的(我们定义的class),这主要是因为要兼容ES5导致的,所以所有的自定义类型对象的typeof值都是object,但是可以用stud.constructor来实现获取其类型(是一个方法类型,兼容ES5)【注意浏览器的EPEL和node的EPEL是有一些不一致的;最好是用Chrome系的EPEL来调试,否则会发现很多是不一致的,Chrome的还是做的不错的】

47.在js和ts里,undefined和null的两个变量如果用==比较是true,如果是用===则是false;

原文地址:https://www.cnblogs.com/silentdoer/p/8921837.html