ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
#### 块级作用域 ``` var a = 3; let a = 4; //error: has already been declared var a = 3; { let a = 4; a; //4 } a; //3 ``` let不允许声明提升 ``` console.log(a); //undefined console.log(b); //ReferenceError typeof a; //undefined typeof b; //ReferenceError var a; let b; ``` 循环引用问题 ``` //用let代替 //方案1 for(let i = 0; i < 10; i++){ ... } //方案2 for(var i = 0; i < 10l i++){ let j = i; .../*操作j*/ } ``` const对于非单值来说,它保证的是地址不变,而不是值不变。 #### spread/rest ``` //spread function foo(x, y, z) { console.log(x, y, z) } foo(...[1, 2, 3]); //1 2 3 var a = [2, 3, 4]; var b = [1, ...a, 5]; //[1, 2, 3, 4, 5] //rest function foo(x, y, ...z) { console.log(x, y, z) } foo(1, 2, 3, 4, 5); //1 2 [3, 4, 5] ``` ``` //es6把形参转化为数组 function foo(...args) { ... } //es5把形参转化为数组 function foo() { var args = Array.prototype.slice.call(arguments); } ``` #### 默认参数值 ``` //es6的默认值相当于x !== undefined ? x : 默认值 function foo(x = 1, y = 2) { ... } //默认参数的赋值作用域位于()中,()中没有w的声明所以向外找到了w,x + 1语句执行时x已经由w + 1生成了,没问题。但是到z = z + 1语句时,z在函数内部无法找到,而z本身又是形参,因此永远不会向外找到那个为2的z。 var w = 1, z = 2; function foo(x = w + 1, y = x + 1, z = z + 1) { console.log(x, y, z); } foo(); //ReferenceError, z is not defined ``` #### 解构 ``` var [a, b, c] = [1, 2, 3]; var {x, y, z} = {x: 1, y: 2, z: 3} //原理 var {x: a, y: b, z: c} = {x: 1, y: 2, z: 3} console.log(x, y, z); //ReferenceError console.log(a, b, c); //1 2 3 ``` ``` var {x, y, z} = {x: 1, y: 2, z: 3} //必须用()包起来,否则{}将会被当作是一个块级作用域,跟{} + [] -> 0一个道理 var x, y, z; ({x, y, z} = {x: 1, y: 2, z: 3}); ``` 巧用解构来交换两个变量而不用临时变量 ``` var x = 10, y = 20; [y, x] = [x, y]; console.log(x, y); //20 10 //[y, x] = [10, 20]; y = 10, x = 20 ``` 数组重排序 ``` var a1 = [1, 2, 3], a2 = []; [a2[2], a2[0], a2[3]] = a1; a2; //[2, 3, 1] ``` 解构允许重复(key重复) ``` var {a: X, a: Y} = {a: 1}; console.log(X, Y); //1 1 var {a: {x: X, x: Y}, a} = {a: {x: 1}}; X; //1 Y; //1 a; //{x: 1} ({a: X, a: Y, a: [Z]} = {a: [1]}); X.push(2); Y[0] = 10; X; //[10, 2] Y; //[10, 2] Z; //1 ``` 解构完成后,返回值依旧是原对象/数组 ``` //数组同理 var o = {a: 1, b: 2, c: 3}; var p = {a, b, c} = o; //{a, b, c} = o解构完成后返回o p === o; //true var o = {a: 1, b: 2, c: 3}; var a, b, c; ({a} = {b, c} = o); console.log(a, b, c); //1 2 3 //{b, c} = o解构后返回o -> {a} = o var p = [4, 5, 4]; [x, y] = [z] = p; console.log(x, y, z); //4 5 4 ``` #### 太多 太少 刚刚好 ``` var [a, b] = [1, 2, 3, 4, 5]; a; //1 b; //2 var [, , c] = [1, 2, 3, 4, 5]; c; //3 //多余的值会被设为undefined var [a, b, c] = [1]; b; //undefined c; //undefined ``` 如果...出现在解构的位置,则执行`集合`操作 ``` var a = [2, 3, 4]; //...必须出现在最后的位置 var [b, ...c] = a; b; //2 c; //[3, 4] ``` 默认解构值 ``` var [a, b, c = 4] = [1]; a; //1 b; //undefined c; //4 ``` 嵌套解构 ``` var a1 = [1, [2, 3, 4], 5]; var o1 = {x: {y: {z: 6}}}; var [a, [b, c, d], e] = a1; var {x: {y: {z: w}}} = o1; a; //1 b; //2 c; //3 d; //4 e; //5 w; //6 //xyz均为ReferenceError not defined ``` ``` var a = []; var [b, c, ...d] = a; b; //undefined c; //undefined a; //[] ``` ``` var defaults = { options: { remove: false, enable: false, instance: {} }, log: { warn: true, error: true } } var config = { options: { remove: false, instance: null } } //es5 config.options = config.options || {} config.options.remove = (config.options.remove !== undefined) ? config.options.remove : defaults.options.remove; config.options.enable = (config.options.enable !== undefined) ? config.options.enable : defaults.options.enable; ... //es6 let { options: { remove = defaults.options.remove, enable = defaults.options.enable, instance = defaults.options.instance } = {}, log: { warn = defaults.log.warn, error = defaults.log.error } = {} } = config; config = { options: {remove, enable, instance}, log: {warn, error} } ``` #### 对象字面量扩展 ``` var x = 2, y = 3; //简写 var o = {x, y}; //简写 var o = {w(), z()}; //生成器的简写 var o = {*foo(){}}; ``` 计算属性 ``` //es5 var o = {}, prefix = "user_" o[prefix + "foo"] = function() { ... }; o[prefix + "bar"] = function() { ... }; //es6 var o = { [prefix + "foo"]: function() { ... }, [prefix + "bar"]: function() { ... }, *[prefix + "baz"]: function() { ... } } ``` 设定prototype ``` //es6方式1 var a = {}; var o = { __proto__: a }; //es6方式2 var o = {}; Object.setPrototypeOf(o, a); ``` super对象 ``` var o1 = { foo() { console.log("o1,foo") } }; var o2 = { foo() { super.foo();console.log("o2.foo"); } }; Object.setPrototypeOf(o2, o1); o2.foo(); //o1.foo o2.foo ``` #### 模板字符串 ``` //模板字符串中回车会自动转为换行符 var text = `Now is time for all good men to come here ` text; //Now is time for // all good men //to come here function upper(s) { return s.toUpperCase(); } var who = "reader"; var text = `A very ${upper(`${who}`)} welcome`; // A very READER welcome ``` 标签模板字面量 ``` function foo(strings, ...values) { console.log(strings); console.log(values); } var desc = "awesome"; foo`Everything is ${desc}!`; //[ "Everything is", "!" ]; //[ "awesome" ]; ``` 原始字符串 ``` var str = "hello\nworld"; str; //hello //world String.raw`hello\nworld`; //hello\nworld; String.raw`hello\nworld`.length; //12 ``` #### 箭头函数 箭头函数的主要设计目的就是以特定的方式改变this的行为,而不是仅仅减少代码量。 箭头函数的this绑定不是动态的,而是词法的。箭头函数没有自己的this, 它的this继承的是父执行上下文里面的this ``` var obj = { say: function () { setTimeout(() => { console.log(this) }); } } obj.say(); // obj //此时的 this继承自obj, 指的是定义它的对象obj, 而不是 window! ``` ``` var obj = { say: function () { var f1 = () => { console.log(this); // obj setTimeout(() => { console.log(this); // obj }) } f1(); } } obj.say(); //因为f1定义时所处的函数 中的 this是指的 obj, //setTimeout中的箭头函数this继承自f1, 所以不管有多层嵌套,都是 obj ``` ``` var obj = { say: function () { var f1 = function () { console.log(this); // window, f1调用时,没有宿主对象,默认是window setTimeout(() => { console.log(this); // window }) }; f1(); } } obj.say(); //都是 window,因为 箭头函数在定义的时候它所处的环境相当于是window, //所以在箭头函数内部的this函数window ``` #### for of循环 for in循环的结果是key,for of循环的结果是value ``` var a = [1, 2, 3, 4]; for(var val of a){ console.log(val); //1 2 3 4 } //原理 var a = [1, 2, 3, 4]; for(var val, ret, it = a[Symbol.iterator](); (ret = it.next()) && !ret.done);){ val = ret.value; console.log(val); } ``` #### 数字字面量扩展 ``` //二进制和八进制表示法 var dec = 42; var oct = 0o52; var hex = 0x2a; var bin = 0b101010; num.toString(n)代表把num转换成n进制的数字的字符串 ``` #### 符号 js新的原生类型:symbol ``` //不能也不应该用new,它并不是构造器 //参数是可选的,它应该是一个Symbol用途的描述 //参数只能是字符串,即便是其他类型,也会强转为字符串 var sym = Symbol("some description"); typeof sym; //"symbol" sym.toString(); //"Symbol(some description)" ``` 如同str不是String的实例一样,sym也不是Symbol的实例 ``` sym instanceof Symbol; //false var symObj = Object(sym); symObj instanceOf Symbol; //true symObj.valueOf() === sym; //true ``` Symbol的主要意义就是独一无二的值 ``` //由于每一个 Symbol 值都是不相等的,这意味着 Symbol 值可以作为标识符。 const EVT_LOGIN = Symbol("event.login"); //用于对象的属性名,就能保证不会出现同名的属性。这对于一个对象由多个模块构成的情况非常有用,能防止某一个键被不小心改写或覆盖。 let ms = Symbol(); const o = {}; o[ms] = "a symbol"; //ms这一属性无法被覆盖 ``` Symbol.for(全局符号注册) Symbol.for(str)会在全局中搜索内容为str的symbol,有的话将其返回,没有的话创建一个并返回 ``` var s = Symbol("regist"); var ms = Symbol.for("login"); ms; //Symbol(login) //直接Symbol()创建的symbol是无法获取内部的内容的 Symbol.keyFor(s); //undefined //使用Symbol.for创建的symbol可以获取内部内容 Symbol.keyFor(ms); //login ``` Symbol作为对象的属性,虽然确保了该属性不会被覆盖,但该属性也不会出现在一般的枚举中,除非使用`getOwnPropertySymbols()` ``` var o = { [Symbol("bar")]: "hello", foo: 1, bar: 2 }; Object.getOwnPropertyNames(o); //["foo", "bar"]; Object.getOwnPropertySymbols(o); //[Symbol(bar)] ```