ES6

201 阅读11分钟

模块化

  • CommonJS规范:它是Node.js中所遵循的模块规范,该规范约定,一个文件就是一个模块,每个模块都有单独的作用域,通过module.exports导出成员,再通过require函数载入模块。

缺点:CommonJS约定的是以同步的方式加载模块,因为Node.js执行机制是在启动时加载模块,执行过程中只是使用模块,所以这种方式不会有问题。但是如果浏览器使用同步的加载模式,会引起大量同步模式请求,导致应用运行效率地下。

  • AMD(Asynchronous Module Definition)规范:异步模块定义规范。有一个出名的库Require.js。社区提出的规范。

缺点:目前绝大多数第三方库都支持 AMD 规范,但是它使用起来相对复杂,而且当项目中模块划分过于细致时,就会出现同一个页面对 js 文件的请求次数过多的情况,从而导致效率降低。

  • ES Modules:ECMAScript 2015(ES6)中定义的模块系统,逐渐被浏览器实现。

CommonJS

CommonJS规范:它是Node.js中所遵循的模块规范,该规范约定,一个文件就是一个模块,每个模块都有单独的作用域,通过module.exports导出成员,再通过require函数载入模块。

exports module.export

exports 只是 module.exports的引用,辅助后者添加内容用的。这等同在每个模块头部,有一行这样的命令:

var exports = module.exports;

好多建议尽量都用 module.exports 导出,然后用require导入。 www.cnblogs.com/shichangchu…

ES Modules

export

  1. export与export default均可用于导出常量、函数、文件、模块等

  2. 在一个文件或模块中,export 、import可以有多个,export default仅有一个

  3. 通过export方式导出,在导入时要加{ },export default则不需要

  4. export能直接导出变量表达式,export default不行 juejin.cn/post/684490…

Symbol

请回答一下JavaScript的数据类型有哪些呢?
基本数据类型:undefined、boolean、string、number、null、Symbol、bigint(7种)
引用数据类型:Object(Array、Function、Date、对象)
基本数据类型存储在栈内存中;
引用数据类型保存在堆内存中。
Symbol是一种基本数据类型。 第一个作用是作为属性名避免属性名冲突;
第二个作用是替代代码中多次使用的字符串(例如:abc),多次使用的字符串在代码中不易维护,而这时候定义一个对象的属性(属性名用Symbol格式),值为abc,就可以作为全局变量来使用了。
第三个,由于以Symbol值作为名称的属性,不会被常规方法遍历得到。我们可以利用这个特性,为对象定义一些非私有的、但又希望只用于内部的方法。
第四个,这个有时,我们希望重新使用同一个Symbol值,Symbol.for方法可以做到这一点。它接受一个字符串作为参数,然后搜索有没有以该参数作为名称的Symbol值。如果有,就返回这个Symbol值,否则就新建并返回一个以该字符串为名称的Symbol值。 zhuanlan.zhihu.com/p/22652486

定义和使用

在对象中使用的三种方式:

  let mySymbol = Symbol();
  //第一种
  let a1 = {};
  a1[mySymbol] = 'hello';

  //第二种
  let a2 = {
      [mySymbol]: 'javascript'
  };

  //第三种
  let a3 = {};
  Object.defineProperty(a3, mySymbol, {value: 'good'});

  console.log(a1[mySymbol], a2[mySymbol], a3[mySymbol] )
  1. 使用 Symbol 值定义属性时,Symbol 值必须放在方括号之中。
  2. Symbol 作为属性名,遍历对象的时候,该属性不会出现在for...in、for...of循环中,也不会被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。
  3. 可以通过 Object.getOwnPropertySymbols(obj) 获取 Symbol 属性名,(不太实用)
  4. 新的 API,Reflect.ownKeys()方法可以返回所有类型的键名,包括常规键名和 Symbol 键名。
    let mySymbol = Symbol('motto');

    let person = {
        name: 'zs',
        age: 12,
        [mySymbol]: 'less code'
    }
    console.log(Object.getOwnPropertySymbols(person));
    for(let key of Reflect.ownKeys(person)){
        console.log(key);
    }

Symbol.for()

Symbol.for()与Symbol()这两种写法,都会生成新的 Symbol。它们的区别是,前者会被登记在全局环境中供搜索,后者不会。Symbol.for()不会每次调用就返回一个新的 Symbol 类型的值,而是会先检查给定的key是否已经存在,如果不存在才会新建一个值。比如,如果你调用Symbol.for("cat")30 次,每次都会返回同一个 Symbol 值,但是调用Symbol("cat")30 次,会返回 30 个不同的 Symbol 值。

Map和Object区别

定义

    //定义
   let map1 = new Map();
   let map2 = new Map([['name', 'zs'], ['age', 14]])
   let obj = {
       name: 'zs',
       age: '44'
   };
   console.log(map1 instanceof Map, map1);    // true
   console.log(typeof map1);                 //object

key值的不同

我们都知道object类型的key只能是字符串、Symbol或者数字(其实也被转为字符串了);
Map的key可以是基本类型以及引用类型。

元素顺序

Map 元素的顺序遵循插入的顺序,而 Object 的则没有这一特性。

数据访问      get/has    VS    . / in

   console.log(map2.get('name'), map2.get('name2'));  //zs   undefined
   console.log(obj.name, obj.name2);       //zs   undefined

   console.log(map2.has('name'));           //true
   console.log('name' in obj);              //true

新增一个数据 set

Map和Object如果key相同都会覆盖。

    map2.set('gender', 'male');
    obj.gender = 'male';

删除数据 delete clear

   map2.delete('gender');
   delete obj.gender;
   obj.address = 'shanghai';
   obj.address = undefined;
   
   console.log(map2, obj)

需要注意的是,使用 delete 会真正的将属性从对象中删除,而使用赋值 undefined 的方式,仅仅是值变成了 undefined。属性仍然在对象上,也就意味着 在使用 for … in… 去遍历的时候,仍然会访问到该属性。

获取size size VS Object.keys(obj).length

    console.log(map2.size, Object.keys(obj).length);

Iterating

具有 [Symbol.iterator] 属性的数据类型才可以进行for...of循环;
object本身没有 [Symbol.iterator] 属性,所以for...of 会报错!

原生具备 Iterator 接口的数据结构如下:
Map
Array
String
Set
函数arguments对象
TypedArray
NodeList对象
话锋一转,怎么循环遍历对象呢? for...in

    for(let t in obj){
       console.log(t);            // name   age   address
   }

blog.csdn.net/ckwang6/art…

var let const区别

区别1:var定义的变量如果不是在函数体里面,那么会挂载在window上,成为它的属性; let const定义的变量即使不在函数体里面,也不会挂载在window上面:

   var name='xyy';
   let age = 29;
   console.log(window.name, window.age);        //xyy undefined

为什么呢?
因为let const定义的变量有块级作用域的作用,let age = 29; 相当于:

   (function(){
   	let age = 29;
   })()

区别2:var定义的变量有变量声明提升的效果,const/let定义的没有

  console.log(a);          //undefined
  var a = 100; 
  console.log(b);          //报错 Uncaught ReferenceError: Cannot access 'b' before initialization
  let b = 100;

Proxy 和 Reflect

Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)

const proxy = new Proxy(target, handler)
  • target 要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组、函数,甚至另一个代理)
  • handler 一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时的代理 p 的行为。
  • Prxoy.revocable() 方法可以创建一个可撤销的Proxy对象。 对于Proxy而言,重点是第二个参数 handler
  1. get(target, propKey, receiver){} 拦截对象属性的读取,如 p.foo 和 p['foo'];receiver 是由 proxy对象提供的,所以 receiver 总是指向proxy对象,即 receiver === proxy
  2. set(target, propKey, value, receiver){} 拦截对象属性的设置,比如 proxy.foo = v 或者 proxy[foo] = v; 返回一个布尔值。
  3. has(target, propkey){} 拦截 propKey in proxy 的操作,返回一个布尔值。
  4. deleteProperty(target, propKey){} 拦截 delete proxy[propKey] 的操作,返回一个布尔值。如果这个方法抛出错误或者返回 false,当前属性就无法被 delete 命令删除。
  5. ownKeys(target){} 拦截 Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for...in循环,返回一个数组。该方法返回目标对象所有自身属性的属性名,而 Object.keys() 返回结果仅包括目标对象自身的可遍历属性。
  6. getOwnPropertyDescriptor(target, propKey) 拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。
  7. defineProperty(target, propKey, propDesc):拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。
  8. preventExtensions(target):拦截Object.preventExtensions(proxy),返回一个布尔值。
  9. getPrototypeOf(target):拦截Object.getPrototypeOf(proxy),返回一个对象。
  10. isExtensible(target):拦截Object.isExtensible(proxy),返回一个布尔值。
  11. setPrototypeOf(target, proto):拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。
  12. apply(target, object, args):拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)。
  13. construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)。 例子:赋值验证
let validator = {
	set: function(obj, prop, value){
    	if(pro === 'age'){
        	if(!Number.isInteger(value)){
            	throw new TypeError('The age is not an integer');
            }
            if(value>200){
            	throw new RangeError('The age seems invalid');
            }
        }
        obj[prop] = value;
        return true;
    }
}
let person = {name: 'Max'};
let proxy = new Proxy(person, validator);

person = 300;  // ok

proxy.age = 'young';
// 抛出异常: Uncaught TypeError: The age is not an integer

proxy.age = 300;
// 抛出异常: Uncaught RangeError: The age seems invalid

console.log(typeof proxy); //object
  • 真正被代理的是 proxy 对象,而不是 目标对象 person,这一点不要搞混;
  • set(target, propKey, value, receiver){} 这里的 target === person; recerver === proxy
  • person 对象修改的时候会同步反应在 proxy 对象上;反过来亦是如此。
person.name = 'William';
proxy.job = 'actor';
console.log(person, proxy);

Reflect 是一个内置对象,它提供了拦截 JavaScript 操作的方法。这些方法与 proxy handlers 的方法相同。 Reflect 不是一个构造对象,因此它是不可构造的。

静态方法:

  1. Reflect.apply(target, thisArgument, argumentsList) 对一个函数进行调用操作,同时可以传入一个数组作为调用参数。和 Function.prototype.apply() 功能类似。
  2. Reflect.construct(target, argumentsList[, newTarget]) 对构造函数进行 new 操作,相当于执行 new target(...args)
  3. Reflect.defineProperty(target, propertyKey, attributes) 和 Object.defineProperty()类似。如果设置成功就会返回 true
  4. Reflect.deleteProperty(target, propertyKey) 作为函数的delete操作符,相当于 delete target[name]
  5. Reflect.get(target, propertyKey[, receiver]) 获取对象身上某个属性的值,类似于 target[name]
  6. Reflect.getOwnPropertyDescriptor(target, propertyKey) 类似于 Object.getOwnPropertyDescriptor()。如果对象中存在该属性,则返回对应的属性描述符, 否则返回 undefined.
  7. Reflect.getPrototypeOf(target) 类似于 Object.getPrototypeOf()。
  8. Reflect.has(target, propertyKey) 判断一个对象是否存在某个属性,和 in 运算符 的功能完全相同。
  9. Reflect.isExtensible(target) 类似于 Object.isExtensible().
  10. Reflect.ownKeys(target) 返回一个包含所有自身属性(不包含继承属性)的数组。(类似于 Object.keys(), 但不会受enumerable影响).
  11. Reflect.preventExtensions(target) 类似于 Object.preventExtensions()。返回一个Boolean。
  12. Reflect.set(target, propertyKey, value[, receiver]) 将值分配给属性的函数。返回一个Boolean,如果更新成功,则返回true。
  13. Reflect.setPrototypeOf(target, prototype) 设置对象原型的函数. 返回一个 Boolean, 如果更新成功,则返回true。

箭头函数

www.jianshu.com/p/a416cb02e…
juejin.cn/post/684490…
www.jianshu.com/p/31ac0f628…
juejin.cn/post/686473…
www.babeljs.cn/
我来总结一下:

  • 箭头函数不能用于构造函数,即不能用 new 关键字调用;
  • 箭头函数没有prototype属性,我们知道构造函数的prototype属性指向原型对象,箭头函数没有prototype是更纯粹的函数;
  • 箭头函数没有argeuments; 可以使用(...rest)=>{}解决参数不定长的问题;
  • 箭头函数不能绑定this,不能改变其this指向;

普通函数 this

const printNumbers = {
  phrase: 'The current value is:',
  numbers: [1, 2, 3, 4],

  loop() {
    this.numbers.forEach(function (number) {
      console.log(this.phrase, number)
    })
  },
}

普通函数并不是通过上下文作用域来决定this的值,而是通过实际调用函数的对象来决定的。

箭头函数 this

  • 箭头函数的外层如果有普通函数,那么箭头函数的 this 就是外层普通函数的this
  • 箭头函数的外层如果没有普通函数,那么箭头函数的 this 就是全局变量

**箭头函数不能绑定this,箭头函数的this在定义的时候就确定了。
不能通过apply call bind 改变其this指向 **

对于上面这句话的理解:

  window.color = "red";
  let color = "green";
  // var color = "black";
  let obj = {
    color: "blue"
  };
  let sayColor = () => {
    return this.color;
  };
  console.log(sayColor.apply(obj));

sayColor是一个箭头函数,applay是不起作用的,所以函数里面的this没有被改变。这里箭头函数里面的this按照定义的时候this的指向的。定义sayColor的时候 this 指向全局window。所以 this.color指的是window.color。同时还有一个知识点就是 let const定义的变量并不会加载到 window 上面的。所以 let color并不会改变 window.color的值。

没有变量指向匿名函数的执行环境具有全局性,因此其 this 对象通常指向 window

什么是没有变量指向的匿名函数?比如:

setTimeout(function(){}, 100)

arr.forEach(function(){})

function(){ return function(){} }

里面的函数没有变量指向它们,这些函数执行的时候执行环境具有全局性,因此其 this 对象通常指向 window。

但是 下面的代码中 obj.getNameFunc 指向了后面的匿名函数,所以obj.getNameFunc执行里面this 指向obj。箭头函数的出现解决了这一弊端。

    var name = "The window";
    var object = {
      name: 'My Object',
      getNameFunc: function () {
      	console.log(this);
        return function () {
            return this.name;
        }
      },
      getName: function () {
        return ()=>{
          return this.name;
        }
      }
    }
    console.log(object.getNameFunc()());
    console.log(object.getName()());

console.log(object.getNameFunc()()); 执行输出 "The window"
console.log(object.getName()()); 执行输出 "My Object"

为什么呢?object.getNameFunc()返回的是一个匿名函数,执行环境具有全局性,所以this指向window
箭头函数体内的 this 对象就是定义时所在的对象,而不是使用时所在的对象。