JS_core_3.ES5、ES6特性

1 ES5

1.1 严格模式

比普通js运行机制要求更严格的模式。几乎所有js都要运行在严格模式下,js语言有广受诟病的很多缺陷,严格模式将不合理的行为直接变为错误;提高效率、增加运行速度。
在所有js文件/script标签顶部: “use strict”;在函数定义内的顶部添加:“use strict”,匿名函数可作为script开启严格模式的变通实现;
严格模式要求:

  1. 禁止给未声明的变量赋值:
  2. 静默失败升级为错误:
    静默失败: 执行不成功,也不报错
  3. 禁用了arguments.callee:
    arguments.callee专门在调用函数时,获得当前函数本身
    禁用是因为递归重复计算量太大,效率极低,所以禁用递归中的arguments.callee,用循环代替
  4. 普通函数调用中的this不再指向window而是undefined

1.2 保护对象

1.2.1 保护属性

普通js中对象的属性值可以是任何值,也可以随意修改
属性可分为:命名属性(可直接用.访问的属性,可分为数据属性和访问器属性)和内部属性(__ proto__、constructor、class)
数据属性:

  属性: {
    value: 实际存储属性值,
    writable: true/false, //控制是否可修改
    enumerable: true/false, //控制是否可被for in遍历
               //只能防住for in,但是用.依然可访问
    configurable: true/false //控制是否可删除该属性
                   //还控制是否可修改另外两个特性
                   //一旦改为false,不可逆!修改其它两个开关同时修改configurable:false作为双保险。
  }

  获取一个属性的四大特性: 
   var miniObj=Object.getOwnPropertyDescriptor(obj, "属性名");
   
  修改一个属性的四大特性: 
   Object.defineProperty(obj,"属性名",{
     开关: true/false,
       ... : ...
   })
  修改多个属性的四大特性: 
   Object.defineProperties(obj,{
     属性名:{
       开关:true/false,
         ... : ...
     },
     ... : {
     }
   })

四大特性除value外,默认值为false
数据属性开关不能用自定义规则保护属性,使用访问器属性:
访问器属性: 不实际存储属性值,仅提供对其他数据属性的保护;访问器属性不能用直接量定义,只能用defineProperty/defineProperties方法添加

使用隐藏的数据属性实际存储数据,再添加一个访问器属性来保护隐藏的数据属性: 
   Object.defineProperty(obj,"访问器属性名",{
     get:function(){ return this.受保护的数据属性 },//获取属性值自动调用get()
     set:function(value){//修改属性值自动调用set()
       //value可接住将来要赋的新值
       //如果value符合条件
		//才this.受保护的数据属性=value
       //否则
        //报错throw new Error("")
     },
     enumerable:true, //让访问器属性代替数据属性抛头露面
     configurable:false
   })
可以绕过访问器直接修改受保护的数据属性值_age,所以需要将受保护的属性值保存在闭包中

构造函数可以同时创建保护多个属性值的对象

function Emp(eid,ename,salary,age){
  Object.defineProperties(this,{
    eid:{
      ...
    },
    _age:{
      writable:true,
      enumerable:false
    }
  })
  this.age=age;//将传入的参数age指向原型对象,否则跳过条件判断直接赋给_age
}
//Emp.prototype.age={//访问器属性只能通过defineProperty定义
Object.defineProperty(Emp.prototype,"age",{
  get:function(){ return this._age; },
  set:function(value){
    if(value>=18&&value<=65)
      this._age=value;
    else throw Error("年龄超范围");
  },
  enumerable:true,
  configurable:false
});

可以将受保护的属性值保存在闭包中,以防止绕过访问器属性,直接修改属性的值,这样一来,访问器属性不能放在原型对象中,最终ES6还是将访问器属性放在原型对象中,默认程序员不随意使用以下划线开头的变量名

1.2.2 保护结构

构造函数创建的对象,保护结构的语句写在构造函数内部,Eg. Object.seal(this)

  1. 防扩展: 禁止给当前对象添加新属性
    Object.preventExtensions(obj)
  2. 密封: 在兼具防扩展的同时,又禁止删除现有属性
    Object.seal(obj)
    原理:
    1. Object.preventExtensions(obj)
    2. 自动将所有属性的configurable都设置为false
  3. 冻结: 在兼具密封的基础上,又禁止修改属性值
    Object.freeze(obj)
    原理:
    1. Object.seal(obj)
    2. 又自动将所有属性的writable都设置为false

1.3 Object

1.3.1 Object.create()

Object.create(): 创建新对象继承现有的指定父对象,同时为新对象添加自有属性

	var child=Object.create(father,{
     //为child添加新自有属性
     //语法相当于defineProperties
     属性名:{
       value:属性值,
       开关: true/false,
         ... : ...
     }
   })
	原理: 1. 创建一个空对象
      2. 让空对象继承father
      3. 为新对象添加自有属性

1.3.2 替换this:

如果一个函数调用时,其中的this不是想要的,就可替换为想要的对象。有三种方式:

  1. call/apply:强行调用指定函数,并临时替换一次this:
    call(): 要求传入函数的参数分别传入
    apply(): 要求传入函数的参数必须放在一个数组中整体传入。
    指定函数.call(替换this的对象,参数值,…)
  2. bind():基于原函数,创建新函数副本,并永久绑定this:
    var 新函数=原函数.bind(替换this的对象, 其他参数…)
    当需要替换回调函数中的this时使用

1.4 数组API

  1. 查找元素indexOf: 查找一个指定元素在数组中的位置
    var i=arr.indexOf(elem[, starti ])
    用法同str.indexOf()
  2. 判断: 数组中的元素是否符合要求:
    1. 判断数组中的元素是否都符合要求:
      var bool=arr.every(function(val, i, arr){
      //val->当前正在遍历到的元素值
      //i->当前正在遍历的位置
      //arr->当前正在遍历的数组对象
      return 判断条件;
      })
      依次判断arr中每个元素,是否都符合“判断条件”的要求
      执行过程: every自动拿着回调函数,在每个元素上执行一次
      每次执行时,都自动传入: 当前元素值,当前位置, 当前数组对象
      每次执行回调函数后,都返回对当前元素的判断结果。
      只有所有元素经过判断都返回true时,every才返回true
    2. 判断数组中是否包含符合要求的元素:
      var bool=arr.some(function(elem,i,arr){
      return 判断条件
      })
  3. 遍历: 依次对数组中每个元素执行相同的操作
    1. 对原数组中的元素执行相同的操作:
      arr.forEach(function(elem,i,arr){
      操作
      })
      会自动拿着回调函数,在每个元素上执行一次!
    2. 复制出原数组中的元素,执行相同操作后,放入新数组中,原数组保持不变。
      var 新数组=arr.map(function(elem, i, arr){
      return 新元素值;
      })
      map: 3件事:
      1. 创建一个新的空数组
      2. 自动在每个元素上调用一次回调函数,将返回的新值,放入新数组中相同位置
      3. 返回新数组
  4. 过滤和汇总:
    1. 过滤: 复制出原数组中符合条件的元素,组成新数组返回
      var 新数组=arr.filter(function(elem,i,arr){
      return 判断条件;
      })
      执行过程: filter会自动拿着回调函数在每个元素上执行一次
      每次执行时,如果当前元素符合“判断条件”要求,就复制到新数组中。
    2. 汇总: 将数组中的所有元素,经过统计,得出一个最终结论
      var result=arr.reduce(function(prev,elem,i,arr){
      return 将当前元素的内容汇总到prev中
      },start);
      //start: 开始的基础值
      //prev: 截止目前位置,之前的元素的临时汇总值

2 ES6

ES6: 不改变原理的基础上,尽量简化代码

2.1 块级作用域

let: 代替var声明变量
js有两个广受诟病的缺陷: 声明提前&没有块级作用域
if while do…while for 都是一级作用域
let块级作用域不会声明提升也不能重复声明
实现原理:块级作用域内自调用匿名函数;变量名前加下划线防止变量名冲突

2.2 参数增强

  1. 默认参数: 即使不提供实参,形参也有备用的参数值
    function fun(形参1, …, 形参n=默认值)
    强调: 带默认值的参数必须定义在参数列表的末尾

  2. 剩余参数: 代替arguments,接收不确定个数的参数值
    arguments的问题:

    1. 不是纯正的数组类型,无法使用数组的API
    2. 只能获得全部参数值,无法有选择的获得部分

    定义函数: function fun(形参1,…数组名){ … }
    执行时: …可自动获得除形参1之外的剩余参数,保存在一个数组中。
    1. …后的数组名获得的是一个纯正的数组
    2. 可有选择的获得部分参数值

  3. 散播spread: 专门执行打散数组传入函数的操作(apply的本职是替换this,顺便打散数组)
    调用函数fun(…数组)
    执行时 …会先将数组打散为单个元素,再分别传入fun中
    ;当不允许使用时,fun.apply(null, 数组)

2.3 箭头函数

箭头函数: 用于简化回调函数和匿名函数的

  1. 去function,改为=>
  2. 如果函数体只有一句话,则可省略{}
    如果仅有的一句话还是return,则必须省略return
  3. 如果只有一个形参,则可省略()

箭头函数特点:
1)一旦改为箭头函数后,内外this一致,如果不希望内外this相同时,就不能用箭头函数;
2)function可以定义构造函数,而箭头函数不能;
3)当不是作为参数时,箭头函数定义需要关键字var(let,const),且不会发生变量提升,故需要定义在调用之前

2.4 模板字符串

动态生成内容提前定义的字符串模板,取代字符串拼接
用反引号``包裹字符串模板,支持换行,不和单双引号冲突,动态内容用${}包裹

2.5 解构

解构: 从一个大的对象中,提取出想要的成员,单独使用

  1. 数组解构: 将一个大的数组中的元素提取出来单独使用:
    var [变量1, 变量2, …]=[元素1, 元素2, …]
    结果: 变量1=元素1; 变量2=元素2;
  2. 对象解构: 从一个对象中提取出想要的成员单独使用
    var {属性1:变量1, 属性2:变量2, …}=
    {属性1:值1, 属性2:值2, …}
    结果: 变量1=值1; 变量2=值2;
    如果保存对象的属性值和变量值相等,可以只写var {变量1,变量2,…}
  3. 参数解构: 其实是对象解构在函数传参时的应用
    传统参数定义是固定个数,固定先后顺序,无法灵活选择需要传入的参数
    1. 定义时: 将参数列表定义为对象语法:
      //function fun({属性1:形参1,属性2:形参2,…})
      function fun({形参1,形参2,…}){ … }//简写,对象解构中变量值和属性值保持一致
    2. 调用时: 将传入的参数放在一个对象中整体传入
      fun({
      属性1: 值1, …
      })
      执行: fun将整个实参对象传给形参对象,形参对象通过解构,从实参对象中抽取对应的参数值。 如果找不到对应的,则形参值默认为undefined,在函数内部定义port=port||"3306"则未传值的情况下赋值3306

2.6 遍历

for…of…: 最简化的遍历数组和类数组对象的方法

  1. for循环: 最灵活的

       for(var i=0;i<arr.length;i++){
         var elem=arr[i];
       }
    
  2. forEach:
    arr.forEach((elem,i,arr)=>{ elem })
    局限: 无法控制循环的方向和步调,只能顺序依次遍历每个元素

  3. for of: 只能获得元素值,无法获得当前位置

       for(var elem of arr){
         //of会依次获得arr中每个元素的值
       }
    

for(var key in obj/关联数组)——自定义下标名
for(var elem of arr/类数组对象)——数字下标

2.7 class类

class: ES6对整个面向对象语法的简化:

  1. 封装:对象直接量的简化:

      var sname="Li Lei", sage=11;
      var lilei={
         sname, //sname: sname,
         sage,  //sage: sage,
         intr(){ ... } //:function(){ ... }
       }
    
  2. 对创建类型的简化:

       //1. 用class{}包裹构造函数和原型对象
       class Student{
         //2. 构造函数名提升为类型名,构造函数统一更名为   constructor
         constructor(sname,sage){
           this.sname=sname;
           this.sage=sage;
         }
         //3. 所有原型对象方法可直接写在class中
         intr(){
          console.log(`I'm ${this.sname}, I'm ${this.sage}`);
         }
       }
    
  3. 继承:

       //1.让子类型继承父类型: Plane extends Enemy
       //不再需要Object.setPrototypeOf
       class Plane extends Enemy{
         constructor(fname,speed,score){
          //2.用super(fname,speed)调用father.constructor
          //不再需要call,不再需要传this
          super(fname,speed);
          this.score=score;//写在构造函数内
         }
       }
    
  4. 静态方法:

    	class 类名{
    	  constructor(){
    	  }
    	  方法(){ ... }
    	  static 静态方法(){ ... }
    	}
    	//调用:类名.静态方法()
    
  5. 访问器属性:

    	class Emp{
    	  constructor(eid,ename,age){
    	    this.eid=eid;
    	    this.ename=ename;
    	    Object.defineProperty(this,"_age",{	
    	      writable:true,
    	      enumerable:false,
    	      configurable:false
    	    })
    	    this.age=age;
    	  }
    	  get age(){return this._age}
    	  set age(value){
    	    if(value>=18&&value<=65)
    	      this._age=value;
    	    else throw Error("年龄超范围");
    	  }
    	}
    

2.8 Promise

代替异步调用中的回调函数(可能导致callback hell:函数不过早地提交给上一个函数)
只要多个异步调用先后执行顺序时就需要使用promise

  1. 定义函数
    在原函数内,用new Promise()包裹所有原代码,再在源代码外层套一层function(){}
    function()中必须接收Promise自带的open开关,在当前函数异步任务调用后,自动打开开关open();

    function fun(){
    	return new Promise(function(open){
    	      异步任务
    	      异步任务执行完:open()
    	})
    }
    
  2. 串联多个任务
    第一个函数().then(第二个函数).then(…)
    中间的then中的函数不要加(),因为不是立刻执行,且中间的函数必须支持Promise

错误处理:
1.new Promise(function(open,err){
err(“错误消息”) //正常执行调用open(),出错调用err()
})
2.在函数1().then().then().catch(function(errMsg){ … })
无论中间哪个then出错,都会执行最后的catch,并将then中err(“错误消息”)传给errMsg。

猜你喜欢

转载自blog.csdn.net/qq_33392141/article/details/85860009
今日推荐