专栏汇总前端基础学习(系列二:JS/ES5->ES6)

747 阅读42分钟

js

JS基础部分

作用域

知识体系

谈到闭包拉扯到垃圾回收机制 阮一峰JavaScript 内存泄漏教程

变量提升与函数提升

JS引擎会在正式执行之前先进行一次预编译,在这个过程中,首先将变量声明及函数声明提升至当前作用域的顶端,然后进行接下来的处理。

  • 变量提升

            function hoistVariable() {
                var foo = foo || 5;
    
                console.log(foo); // 5
            }
    
    ---》预编译之后
    
        function hoistVariable() {
            var foo;
    
            foo = foo || 5;
    
            console.log(foo); // 5
        }
    
  • 函数提升

       function hoistFunction() {
           foo(); // output: I am hoisted
    
           function foo() {
               console.log('I am hoisted');
           }
       }
    

    ------》编译之后

       function hoistFunction() {
           function foo() {
               console.log('I am hoisted');
           }
    
           foo(); // output: I am hoisted
       }
    

闭包

闭包就是能够读取其他函数内部变量的函数,【在本质上,闭包是将函数内部和函数外部连接起来的桥梁】

        function f1(){

        var n=999;

          function f2(){
            alert(n); 
            }

            return f2;
          }

        var result=f1();
        result(); // 999
  • 闭包的用途 什么是闭包?闭包的作用,用法及优缺点

    • 可以读取函数内部的变量

    • 让这些变量的值始终保持在内存中

            function f1(){
            nAdd=function(){n+=1} //nAdd是全局函数
          }
      
  • 使用闭包的注意点

    • (1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除

                function foo() {
                     let a = 2;
                 
                     function bar() {
                         console.log( a );
                     }
                 
                     return bar;
                 }
                 
                 let baz = foo();
                 
                 baz(); //baz指向的对象会永远存在堆内存中
                 
                 baz = null; //如果baz不再使用,将其指向的对象释放
      

垃圾回收机制

  • 垃圾回收机制怎么知道,哪些内存不再需要呢?:

    最常使用的方法叫做"引用计数"(reference counting):语言引擎有一张"引用表",保存了内存里面所有的资源(通常是各种值)的引用次数。如果一个值的引用次数是0,就表示这个值不再用到了,因此可以将这块内存释放

      const arr = [1, 2, 3, 4];
      console.log('hello world');
    

    上面代码中,数组[1, 2, 3, 4]是一个值,会占用内存。变量arr是仅有的对这个值的引用,因此引用次数为1。尽管后面的代码没有用到arr,它还是会持续占用内存。

    如果增加一行代码,解除arr对[1, 2, 3, 4]引用,这块内存就可以被垃圾回收机制释放了

      let arr = [1, 2, 3, 4];
      console.log('hello world');
      arr = null;
    

柯里化函数:一种将使用多个参数的一个函数转换成一系列使用一个参数的函数的编程思想

柯里化,可以理解为提前接收部分参数,延迟执行,不立即输出结果,而是返回一个接受剩余参数的函数。因为这样的特性,也被称为部分计算函数。柯里化,是一个逐步接收参数的过程。在接下来的剖析中,你会深刻体会到这一点。

一道经典面试题multi(2)(3)(4)=24?

  • 实现 multi 函数去计算出结果值呢?脑海中首先浮现的解决方案是,闭包

           function multi(a) {
           return function(b) {
               return function(c) {
                   return a * b * c;
               }
           }
       }
    
  • 实现方案存在的缺陷

    • 代码不够优雅,实现步骤需要一层一层的嵌套函数。
    • 可扩展性差,假如是要实现 multi(2)(3)(4)...(n) 这样的功能,那就得嵌套 n 层函数。
  • 使用函数柯里化 解决

      //实现乘积功能,返回得是函数
      function multi() {
          var args = Array.prototype.slice.call(arguments);//获取到实参借用数组的slice方法
          var fn = function() {  //解决多个括号的问题//multi(2,3,4,5)(1,2,7,5)
              var newArgs = args.concat(Array.prototype.slice.call(arguments));//获取数组
              return multi.apply(this, newArgs);//调用下面的方法 
          }
          //返回的结果转字符串,每一次会先执行这个核心方法
          fn.toString = function() { //multi(2,3,4,5)
              //数组reduce方法相称
              return args.reduce(function(a, b) {
                  return a * b;
              })
          }
          return fn;//多个括号调用第一个方法fn,否者直接返回
      }
    
  • compose组合函数 JavaScript专题之函数组合 /// 组合 (compose) /// JS 函数式编程指南

this指向与原型

继承

原型链继承

最常见的继承方法就是使用原型链实现继承

    //父类
    function SuperType() {
      this.property = true;
    }
    SuperType.prototype.getSuperValue = function() {
      return this.property;
    }
    //子类
    function SubType() {
      ths.subproperty = true;
    }
    //继承
    SubType.prototype = new SuperType();// 实现继承

    SubType.prototype.getSubValue = function() {
      return this.subprototype;
    }
    //实例化
    var instance = new SubType();
    console.log(instance.getSuperValue());// true
  • 继承原理 子类SubType默认提供的原型===》它换了一个新原型(即父类SuperType的实例)===》子类SubType具有父类SuperType实例所拥有的全部实现和方法(指向父类SuperType的原型)=======》instance实例具有父类SuperType的subproperty属性

  • 通过原型链寻找原理 调用实例之后的instance的getSuperValue() ===》instance实例上找不到该方法(即子类SubType)====》 顺着原型链先找到SubType.prototype===》顺着原型链找到SuperType.prototype。

如果该属性为引用类型时,所有的实例都会共享该属性,一个实例修改了该属性,其它实例也会发生变化,同时,在创建子类型时,我们也不能向超类型的构造函数中传递参数

借用构造函数

(由原型继承延申至此)为了解决原型中包含引用类型值所带来的问题,开发人员开始使用借用构造函数的技术实现继承,该方法主要是通过apply()和call()方法,在子类型构造函数的内部调用超类型构造函数,从而解决该问题。

        function SuperType(name,colors) {
            this.name=name;
            this.colors = ["red","blue","green"]
        }
        function SubType(name,colors) {
          SuperType.call(this,name,colors);// 实现继承
          this.age = 29;
          this.name = "Nasx";
        }
        var instance1 = new SubType();
        var instance2  = new SubType();
        instance2.colors.push("black");
        console.log(instance1.colors);// red,blue,green
        console.log(instance2.colors);// red,blue,green,black
        console.log(instance2.name);// 

通过使用call()方法,我们实际上是在新创建的SubType实例的环境下调用了SuperType的构造函数,因此,colors属性是分别存在instance1和instance2实例中的,修改其中一个不会影响另一个。

  • call和apply的区别:用来改变this的指向的

    • 传参列表:第一位传的都是改变this指向的那个,第二位call可以一位一位的传实参列表进去,但是,apply只能传一位,而且必须是数组形式的实参。

    • call需要把实参按照形参的个数传进去,

    • apply需要传一个arguments

    • bind() //返回的是方法,bind的时候传的参数会预先传给返回的方法,调用方法时就不用再传参数了。

        function Car(wheelSize,style,c,sitColor,height,width,len){
                    Wheel.apply(this,[wheelSize,style]);
                    Sit.apply(this,[c,sitColor]);
                    Model.apply(this,[height,width,len]);
               }
      
    • 优点:解决了原型链继承中引用类型的共享问题,同时可以在子类型构造函数中向超类型构造函数传递参数。

    • 缺点:定义方法时,将会在每个实例上都会重新定义,不能实现函数的复用。

组合继承

  • 组合继承主要是将原型链和借用构造函数的技术组合到一块

    • 原型链--->实现对原型属性和方法的基础

    • 借用构造函数--->实现对实例属性的基础

         //先通过借用构造函数call实现实例属性的基础
         function SuperType(name) {
           this.name = name;
           this.colors = ["red","blue","green"]
         }
         SuperType.prototype.sayName = function() {
           console.log(this.name);
         }
         function SubType(name,age) {
           SuperType.call(this,name);
           this.age = age;
         }
         //借用原型链实现对原型的属性和方法
         SubType.prototype = new SuperType();
         SubType.prototype.constructor = SubType;
         SubType.prototype.sayAge = function() {
           console.log(this.age);
         }
      
  • 这边先解决原型链/原型

    clipboard.png

原型式继承:creat()

        var person = {
          name: "Nicholas",
          friends: ["Shelby","Court","Van"]
        }
        var anotherPerson = Object.create(person, {
          name: {
            value: "Greg"
          }
        });
        console.log(anotherPerson.name); // Greg

让anotherPerson继承了person,其中,friends作为引用类型,将会被所有继承该对象的对象所共享,而通过传入第二个参数,我们可以定义额外的属性,修改person中的原有信息。

  • 缺点:原型式继承中包含引用类型的属性始终都会共享相应的值。

寄生式继承

创建一个仅用于封装继承过程的函数,该函数在内部以某种方法来增强对象,最后再返回该对象。

        function createAnother(original) {
          var clone = Object(original);      
          // 通过调用函数创建一个新对象
          clone.sayHi = function() {
            console.log("hi");
          }
          return clone;
        }

看做是传进去一个对象,然后对该对象进行一定的加工,也就是增加一些方法来增强该对象,然后再返回一个包含新方法的对象的一个过程

    var person = {
      name: "Nicholas",
      friends:["Shelby","Court","Van"]
    }
    var anotherPerson = createAnother(person);
    anotherPerson.sayHi(); // hi

person是没有包含任何方法的,而通过将person传进去createAnother方法中进行加工,返回的新对象就包含了一个新的方法

  • 缺点:不能实现函数的复用。

寄生组合式继承

组合继承是js中最经常用到的一种继承方法,而我们前面也已经说了组合继承的缺点,组合继承需要调用两次超类型构造函数,一次是在创建子类型原型的时候,另一次是在子类型构造函数内部,子类型最终会包含超类型对象的全部实例属性,但是我们不得不在调用子类型构造函数时重写这些属性。

        function SuperType(name) {
          this.name = name;
          this.colors = ["red","blue","green"]
        }
        SuperType.prototype.sayName = function() {
          console.log(this.name);
        }
        function SubType(name,age) {
          SuperType.call(this,name); // 第二次调用超类型构造函数
          this.age = age;
        }
        SubType.prototype = new SuperType(); // 第一次调用超类型构造函数
        SubType.prototype.constructor = SubType;
        SubType.prototype.sayAge = function() {
          console.log(this.age);
        }

结果就是在SubType.prototype和SubType的实例上都会创建name和colors属性,最后SubType的实例上的name和colors属性会屏蔽掉SubType.prototype上的name和colors属性

寄生组合式继承主要通过借用构造函数来继承属性,通过原型链的混成形式来继承方法,其实就是不必为了指定子类型的原型而调用超类型的构造函数,只需要超类型原型的一个副本就可以了。

            function inheritPrototype(subType,SuperType) {
              var prototype = Object(SuperType); // 创建对象
              prototype.constructor = subType; // 增强对象
              subType.prototype = prototype; // 指定对象
            }


            function SuperType(name) {
              this.name = name;
              this.colors = ["red","blue","green"]
            }
            SuperType.prototype.sayName = function() {
              console.log(this.name);
            }
            function SubType(name,age) {
              SuperType.call(this,name);
              this.age = age;
            }
            //避免了在SubType.prototype上面创建的不必要的,多余的属性
            inheritPrototype(SubType,SuperType);
            SubType.prototype.sayAge = function() {
              console.log(this.age);
            }

es6中的继承

es6中可以使用Class来创建对象,而同样的道理,在es6中,也新增加了extends实现Class的继承,Class 可以通过extends关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多

    class Point {}
    class ColorPoint extends Point {}

上面这个例子中可以实现ColorPoint类继承Point类,这种简洁的语法确实比我们上面介绍的那些方法要简洁的好多呀。

        class Point {  
          constructor(x,  y) {    
            this.x = x;    
            this.y = y;  
          }
        }
        class ColorPoint extends Point {  
          constructor(x,  y,  color) {    
            this.color = color; // ReferenceError    
            super(x, y);    
            this.color = color; // 正确  
          }
        }

后面还会讲到类比继承

异步

解决所有异步的方式

Snipaste_2019-08-17_18-08-10.png

RESful API

看这个就好了 RESTful 架构风格概述

ES5类型的各类方法属性:(会提及全局的内置对象)

Array:迭代方法思否

forEach 数组遍历

      let arr=[1,2,3,4,5];
    //forEach  s数组遍历 可传值
    arr.forEach(function(value,index){
        console.log(value);
        console.log(index)
    })

map,返回数组,数组遍历

       let arr1=arr.map(function(value){
        return value*2+1
            } )

filter对数组的值进行选择性的返回

        let arr2=arr.filter(function(value){
                    return value>2
                })
                console.log(arr2)

some 只要有一个条件满足就返回true

             let arr3=arr.some(function(value){
                return value>5
                })

every 所有的满足

            let arr4=arr.every(function(value){
                        return value>0
                    })
                    console.log(arr4)//true

indexOf()返回数组下标 [lasTindexOf()相同]

                console.log(arr.indexOf(5));
                if(arr.indexOf(5)>1){
                    console.log("正确")
                }

Snipaste_2019-08-14_16-36-34.png
Snipaste_2019-08-14_16-37-35.png

reduce() 计算数组和 【reduceRight()相同从右边计算】

            let arr=[1,2,3,4,5];
                    let result=arr.reduce(function(last,now){
                        return last+now;
                    },0)//后边跟开始跟那个计算
                    console.log(result)//15

String

trim:去除字符串的空白

Number方法

(Number的属性不用强调)=》检测有穷数/检测整数/转化浮点数/转化整数/转化字符串/返回对象原始值

isFinite(value):

方法用来检测传入的参数是否是一个有穷数

ps:返回布尔类型:

       Number.isFinite(NaN) //false
       Number.isFinite(Infinity);  // false

(全局的 isFinite(),会强制转化类型)不会强制将一个非数值的参数转换成数值,这就意味着,只有数值类型的值,且是有穷的(finite),才返回 true

Number.isInteger(value):

方法用来判断给定的参数是否为整数

ps:返回布尔类型

        Number.isInteger("10");      // false
        Number.isInteger(-100000);   // true

注意 NaN 和正负 Infinity 不是整数。

  • Number.isSafeInteger():表示给定的值是否是一个安全整数(safe integer)。不多用

  • Number.parseFloat(string):被解析的字符串

    ps:一个字符串解析成浮点数。该方法与全局的 parseFloat() 函数相同,对(**首字符不可为非数值**)字母**会进行隐式转化删除**
    
           Number.parseFloat("sss")  //NaN
           Number.parseFloat(true)  //NaN
    

给定值被解析成浮点数,如果无法被解析成浮点数,则返回NaN

Number.parseInt(string[, radix]) :

方法依据指定基数 [ 参数 radix 的值],把字符串 [ 参数 string 的值] 解析成整数。(指定转化什么进制的)

ps:一个字符串可以解析指定的多少进制,对(首字符不可为非数值)字母会进行隐式转化删除

            Number.parseInt('0110', 2)   //6
            Number.parseInt('0110', 10) //110
            Number.parseInt('1000s')  //1000
            Number.parseInt('d1d0d00s') //NaN

reaix可有可无,radix为指定基数(即字符串以多少进制的数字表示),默认十进制

Number.prototype.toString([radix])

返回指定 Number 对象的字符串表示形式

ps:---radix指定返回多少进制

            (18).toString()  //"18"
             18.toString()  //Invalid or unexpected token
            (6).toString(2)  // '110'

Number 对象覆盖了 Object 对象上的 toString() 方法,它不是继承的 Object.prototype.toString()。对于 Number 对象,toString() 方法以指定的基数返回该对象的字符串表示。

进行数字到字符串的转换时,建议用小括号将要转换的目标括起来,防止出错。

Number.prototype.valueOf()

返回一个被 Number 对象包装的原始值。

ps:该方法通常是由 JavaScript 引擎在内部隐式调用的,而不是由用户在代码中显式调用的。

            var numObj = new Number(10);
            console.log(typeof numObj); // object

            var num = numObj.valueOf();
            console.log(num);           // 10
            console.log(typeof num);    // number

Function

细讲重要的属性和方法 ---------- 获取函数名称/长度/调用函数caller/apply数组/call 列表/bind 函数

以下是Function属性

Function.caller:返回调用指定函数的函数

返回调用指定函数的函数(函数A使用Function.caller,函数B调用函数A【Function.caller返回函数B】) 掘金

ps : arguments.callee.caller替代了被废弃的 arguments.caller 【callee返回正在执行的函数对象】

        function myFunc() {
               if (myFunc.caller == null) {
                  return ("该函数在全局作用域内被调用!");
               } else
                  return ("调用我的是函数是" + myFunc.caller);
            }


            function ss(){
            	console.log(myFunc())
            }

            ss() // 调用我的是函数是function ss(){console.log(myFunc())}

如果一个函数f是在全局作用域内被调用的,则f.caller为null,相反,如果一个函数是在另外一个函数作用域内被调用的,则f.caller指向调用它的那个函数

Function.length:函数的形参个数

属性指明函数的形参个数

ps:length 是函数对象的一个属性值,指该函数有多少个必须要传入的参数,即形参的个数

            console.log((function(a, b)    {}).length); /* 2 etc. */

            console.log((function(...args) {}).length) // 0, 其他参数不计算在内

            console.log((function(a, b = 1, c) {}).length);
            // 1, b=1是计算,阻断了计算
            // 只有一个a

【arguments.length 是函数被调用时实际传参的个数】,Function.prototype 对象的 length 属性值为 0

Function.name:返回函数实例的名称。

  • 函数声明的名称:(直接返回名称)

            function doSomething() { }
            doSomething.name;  // "doSomething"
    
  • 构造函数的名称 :变量和方法可以从句法位置推断匿名函数的名称

    ps:不能通过此方法赋值更改函数的名称,此属性是只读的,要更改它,可以使用Object.defineProperty()

           var f = function() {};
               var object = {
                 someMethod: function() {}
                 someMethods: function object_someMethod() {}
               };
    
               console.log(f.name); // "f"
               console.log(object.someMethod.name); // "someMethod"
               console.log(object.someMethods.name); // "object_someMethod"
    
    • 简写方法的名称

               var o = {
                 foo(){}
               };
               o.foo.name; // "foo";
      
    • 绑定函数的名称 : Function.bind() 所创建的函数将会在函数的名称前加上"bound " 。

               function foo() {}; 
               foo.bind({}).name; // "bound foo"
      

以下是Function属性

  • Function 方法:

Function.prototype.apply():改变this指向(数组)

【数组】调用一个具有给定this值的函数,以及作为一个数组(或类似数组对象)提供的参数。

ps:call()方法的作用和 apply() 方法类似,区别就是call()方法接受的是参数列表,而apply()方法接受的是一个参数数组。

            var numbers = [5, 6, 2, 3, 7];

            var max = Math.max.apply(null, numbers);

            console.log(max);
            // expected output: 7
  • 语法:function.apply( thisArg , [ argsArray ] )

    • thisArg: 可选的。在 func 函数运行时使用的** this** 值来改变this指向

    • argsArray:可选的。一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 func 函数。如果该参数的值为null 或 undefined,则表示不需要传入任何参数

    调用有指定this值和参数的函数的结果。

    用 apply 将数组添加到另一个数组(concat确实具有我们想要的行为,但它实际上并不附加到现有数组,而是创建并返回一个新数组)

            var array = ['a', 'b'];
            var elements = [0, 1, 2];
            array.push.apply(array, elements);
            console.info(array); // ["a", "b", 0, 1, 2]
    

Function.prototype.call():改变this指向(多个参数)

方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数

  • 经典

-------------------------------------------------使用 call 方法调用父构造函数

               function Product(name, price) {
                  this.name = name;
                  this.price = price;
               }

               function Food(name, price) {
               //调用父构造函数
                  Product.call(this, name, price);
                  this.category = 'food';
               }

              console.log(new Food('cheese', 5).name);
             // expected output: "cheese"

-------------------------------------------------使用 call 方法调用匿名函数

                var animals = [
              { species: 'Lion', name: 'King' },
              { species: 'Whale', name: 'Fail' }
            ];

            for (var i = 0; i < animals.length; i++) {
              (function(i) {
                this.print = function() {
                  console.log('#' + i + ' ' + this.species
                              + ': ' + this.name);
                }
                this.print();
              }).call(animals[i], i);
            }

-------------------------------------------------使用 call 方法调用函数并且指定上下文的 'this'

        function greet() {
          var reply = [this.animal, 'typically sleep between', this.sleepDuration].join(' ');
          console.log(reply);
        }

        var obj = {
          animal: 'cats', sleepDuration: '12 and 16 hours'
        };

        greet.call(obj);  // cats typically sleep between 12 and 16 hours

Function.prototype.bind():改变指向(返回函数,设置预设值)

创建一个新的函数,在bind()被调用时,这个新函数的this被bind的第一个参数指定,其余的参数将作为新函数的参数供调用时使用

-----------------------------------------------------例子

             var module = {
              x: 42,
              getX: function() {
                return this.x;
              }
            }

            var unboundGetX = module.getX;
            console.log(unboundGetX()); // The function gets invoked at the global scope
            // expected output: undefined

            var boundGetX = unboundGetX.bind(module);
            console.log(boundGetX());
            // expected output: 42

---------------------------------------------------实现bind

         //判断浏览器是否function内置bind方法
        if(!Function.prototype.bind){
        Function.prototype.bind = function(oThis) {
              if(typeof this !== 'function'){
                      throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable')
                 }
                 //获取参数,从1开始(为啥呢,因为bind方法有个this指向问题)
               var args=Array.prototype.slice.call(arguments,1),
                   fToBind = this, //指向的是绑定的函数
                   fNOP=function(){},//定义空函数(因为返回的是一个函数)
                   fBound=function(){  
                     // this instanceof fBound === true时,说明返回的fBound被当做new的构造函数调用
                    return fToBind.apply(this instanceof fBound?this:oThis,Args.concat(Array.prototype.slice.call(arguments))) //借用方法
                     }
                    // 维护原型关系(this指向不是window)
                if (this.prototype) {
                  // Function.prototype doesn't have a prototype property
                  fNOP.prototype = this.prototype; 
                }
                // 下行的代码使fBound.prototype是fNOP的实例,因此
                // 返回的fBound若作为new的构造函数,new生成的新对象作为this传入fBound,新对象的__proto__就是fNOP的实例
                fBound.prototype = new fNOP();

                return fBound;
               }
          }

Function.prototype.toString():当前函数源代码的字符串

方法返回一个表示当前函数源代码的字符串。

Object

Object.prototype:返回Object方法与属性

几乎所有的 JavaScript 对象都是 Object 的实例;一个典型的对象继承了Object.prototype的属性(包括方法)

Snipaste_2019-08-14_10-16-24.png

Object.prototype.constructor:返回实例的构造器

返回创建实例对象的 Object 构造函数的引用。注意,此属性的值是对函数本身的引用,而不是一个包含函数名称的字符串

//实例对象是Object

Snipaste_2019-08-14_10-37-43.png
Snipaste_2019-08-14_10-40-50.png

            var o = {};
            o.constructor === Object; // true

            var o = new Object;
            o.constructor === Object; // true

以下是Object的方法

Object.assign():合并拷贝对象

方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。

  • 用法

    1. 复制一个对象

           const obj = { a: 1 };
           const copy = Object.assign({}, obj);
           console.log(copy); // { a: 1 }
      
    2. 深拷贝问题:深拷贝属性值(对象里的属性),浅拷贝对象(对象里的对象)

           let obj1 = { a: 0 , b: { c: 0}}; 
           let obj2 = Object.assign({}, obj1); 
           console.log(JSON.stringify(obj2)); // { a: 0, b: { c: 0}} 
      
           obj2.b.c = 3; 
           console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 3}} 
           console.log(JSON.stringify(obj2)); // { a: 2, b: { c: 3}}
      
    3. 合并对象

           const o1 = { a: 1 };
           const o2 = { b: 2 };
           const o3 = { c: 3 };
      
           const obj = Object.assign(o1, o2, o3);
           console.log(obj); // { a: 1, b: 2, c: 3 }
           console.log(o1);  // { a: 1, b: 2, c: 3 }, 注意目标对象自身也会改变。
      
    4. 合并具有相同属性的对象

           const o1 = { a: 1, b: 1, c: 1 };
           const o2 = { b: 2, c: 2 };
           const o3 = { c: 3 };
      
           const obj = Object.assign({}, o1, o2, o3);
           console.log(obj); // { a: 1, b: 2, c: 3 }
      
    5. 原始类型会被包装为对象

           const v1 = "abc";
           const v2 = true;
           const v3 = 10;
           const v4 = Symbol("foo")
      
           const obj = Object.assign({}, v1, null, v2, undefined, v3, v4); 
           // 原始类型会被包装,null 和 undefined 会被忽略。
           // 注意,只有字符串的包装对象才可能有自身可枚举属性。
           console.log(obj); // { "0": "a", "1": "b", "2": "c" }
      
    6. 异常会打断后续拷贝任务

Object.create():提供新创建的对象的__proto__

创建一个新对象,使用现有的对象来提供新创建的对象的__proto__((指定_proto_)可用来指定原型)

  • 语法:Object.create(proto[, propertiesObject])

    • proto原型对象
    • propertiesObject:可选,添加到新创建对象的可枚举属性(即其自身定义的属性,而不是其原型链上的枚举属性

------------继承之原型式继承

           var person={  
                 name:"Nicho",  
                 friends:["Shelby","Count","Van"]
             }  
             var anthor=Object.create(person,{  
                   name:{  
                     value:"Geral"
                     }
               })
        console.log(anthor.name) //Geral

----------------继承之类比继承

            // Shape - 父类(superclass)
            function Shape() {
              this.x = 0;
              this.y = 0;
            }

            // 父类的方法
            Shape.prototype.move = function(x, y) {
              this.x += x;
              this.y += y;
              console.info('Shape moved.');
            };

            // Rectangle - 子类(subclass)
            function Rectangle() {
              Shape.call(this); // call super constructor.
            }

            // 子类续承父类
            Rectangle.prototype = Object.create(Shape.prototype);
            Rectangle.prototype.constructor = Rectangle;

            var rect = new Rectangle();

            console.log('Is rect an instance of Rectangle?',
              rect instanceof Rectangle); // true

Object.defineProperties():对象上定义一个或多个新的属性或修改现有属性,并返回该对象

方法直接在一个对象上定义一个或多个新的属性或修改现有属性,并返回该对象 学习

  • 语法: Object.defineProperties(obj, props)

    • obj: 将要被添加属性或修改属性的对象

    • props: 该对象的一个或多个键值对定义了将要为对象添加或修改的属性的具体配置

              var obj = {};
             Object.defineProperties(obj, {//数据描述符
               'property1': {
                 value: true,
                 writable: true
               },
               'property2': {
                 value: 'Hello',
                 writable: false
               }
             });
      

  • 数据(数据述符)属性

    • configurable:为false,则不可使用delete操作符(在严格模式下抛出错误), 修改所有内部属性值会抛出错误,在《javaScript高级教程中》说只可以改变writable的值,现在改变writable的值也会抛出错误,默认为 false。

    • Enumerable:该属性是否可枚举,即是否通过for-in或Object.keys()返回属性,如果直接使用字面量定义对象,默认为 false

    • value:与属性关联的值。可以是任何有效的JavaScript值(数字,对象,函数等),默认为 undefined.

    • writable:当且仅当该属性的writable为true时,value才能被赋值运算符改变。默认为 false。

Object.defineProperty():直接在一个对象上定义一个新属性,或者修改一个对象的现有属性

方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。

如果不指定configurable, writable, enumerable ,则这些属性默认值为false,如果不指定value, get, set,则这些属性默认值为undefined

  • 语法:Object.defineProperty(obj, prop, descriptor)

  • obj: 需要被操作的目标对象

  • prop: 目标对象需要定义或修改的属性的名称

  • descriptor: 将被定义或修改的属性的述符

            var o = {}; // 创建一个新对象
    
            // 在对象中添加一个属性与数据描述符的示例(数据描述符)
            Object.defineProperty(o, "a", {
              value : 37,
              writable : true,
              enumerable : true,
              configurable : true
            });
    
            // 对象o拥有了属性a,值为37
    
            // 在对象中添加一个属性与存取描述符的示例(存取数据夫)
            var bValue;
            Object.defineProperty(o, "b", {
              get : function(){ //获取操作
                return bValue;
              },
              set : function(newValue){ //拦截操作
                bValue = newValue;
              },
              enumerable : true,
              configurable : true
            });
    
            o.b = 38; //  newValue为38  o.b也是38  
    

  • 存取描述符属性

    • configurable:为false,则不可使用delete操作符(在严格模式下抛出错误), 修改所有内部属性值会抛出错误,在《javaScript高级教程中》说只可以改变writable的值,现在改变writable的值也会抛出错误,默认为 false。

    • Enumerable:该属性是否可枚举,即是否通过for-in或Object.keys()返回属性,如果直接使用字面量定义对象,默认为 false

    • get:一个给属性提供 getter 的方法,如果没有 getter 则为 undefined。当访问该属性时,该方法会被执行,方法执行时没有参数传入,但是会传入this对象(由于继承关系,这里的this并不一定是定义该属性的对象)。

    • Set:一个给属性提供 setter 的方法(给对象属性设置值时调用的函数),如果没有 setter 则为 undefined。该方法将接受唯一参数,并将该参数的新值分配给该属性。默认为 undefined

configurable enumerable value writable get set
数据描述符 Yes Yes Yes Yes No No
存取描述符 Yes Yes No No Yes Yes

如果一个描述符不具有value,writable,get 和 set 任意一个关键字,那么它将被认为是一个数据描述符。如果一个描述符同时有(value或writable)和(get或set)关键字,将会产生一个异常。

Object.entries():(对象变数组)

返回一个给定对象自身可枚举属性的键值对数组,其排列与使用 for...in 循环遍历该对象时返回的顺序一致(区别在于 for-in 循环也枚举原型链中的属性)。

            const obj = { foo: 'bar', baz: 42 };
            console.log(Object.entries(obj)); // [ ['foo', 'bar'], ['baz', 42] ]

            // array like object
            const obj = { 0: 'a', 1: 'b', 2: 'c' };
            console.log(Object.entries(obj)); // [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ]

            // array like object with random key ordering
            const anObj = { 100: 'a', 2: 'b', 7: 'c' };
            console.log(Object.entries(anObj)); // [ ['2', 'b'], ['7', 'c'], ['100', 'a'] ]

Object.freeze():冻结一个对象

冻结一个对象。一个被冻结的对象再也不能被修改;冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。此外,冻结一个对象后该对象的原型也不能被修改。freeze() 返回和传入的参数相同的对象。

Object.fromEntries():数组变对象

方法接收一个键值对的列表参数,并返回一个带有这些键值对的新对象

  • Map 转化为 Object

          const map = new Map([ ['foo', 'bar'], ['baz', 42] ]);
          const obj = Object.fromEntries(map);
          console.log(obj); // { foo: "bar", baz: 42 }
    
  • Array 转化为 Object

          const arr = [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ];
          const obj = Object.fromEntries(arr);
          console.log(obj); // { 0: "a", 1: "b", 2: "c" }
    

Object.getOwnPropertyDescriptor():返回指定对象上一个自有属性对应的属性描述符

返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)

                var person = {
                name: '张三',
                age: 18
            }

            var desc = Object.getOwnPropertyDescriptor(person, 'name'); 
            console.log(desc)  结果如下
            // {
            //     configurable: true,
            //     enumerable: true,
            //     writable: true,
            //     value: "张三"
            // }

Object. getOwnPropertyDescriptors():所指定对象的所有自身属性的描述符

所指定对象的所有自身属性的描述符,如果没有任何自身属性,则返回空对象

            var person = {
                name: '张三',
                age: 18
            }
            var desc = Object.getOwnPropertyDescriptors(person);
            console.log(desc) 

Object.getOwnPropertyNames():指定对象的所有自身属性的属性名

返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性不包括Symbol值作为名称的属性)组成的数组。

ps:返回一个数组,该数组对元素是 obj自身拥有的枚举或不可枚举属性名称字符串。 数组中枚举属性的顺序与通过 for...in 循环(或 Object.keys)迭代该对象属性时一致。数组中不可枚举属性的顺序未定义。

    var arr = ["a", "b", "c"];
    console.log(Object.getOwnPropertyNames(arr).sort()); // ["0", "1", "2", "length"]

    // 类数组对象
    var obj = { 0: "a", 1: "b", 2: "c"};
    console.log(Object.getOwnPropertyNames(obj).sort()); // ["0", "1", "2"]

Object.getPrototypeOf() 方法返回指定对象的原型

    var proto = {};
    var obj = Object.create(proto);
    Object.getPrototypeOf(obj) === proto; // true

Object.isExtensible() 方法判断一个对象是否是可扩展的

Object.isFrozen()方法判断一个对象是否被冻结。

Object.isSealed() 方法判断一个对象是否被密封。

Object.keys() :方法会返回一个由一个给定对象的自身可枚举属性组成的数组

方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和使用 for...in 循环遍历该对象时返回的顺序一致 。如果对象的键-值都不可枚举,那么将返回由键组成的数组。

  • 语法:Object.keys(obj)
    • obj:要返回其枚举自身属性的对象。

             // simple array
             var arr = ['a', 'b', 'c'];
             console.log(Object.keys(arr)); // console: ['0', '1', '2']
      
             // array like object
             var obj = { 0: 'a', 1: 'b', 2: 'c' };
             console.log(Object.keys(obj)); // console: ['0', '1', '2']
      
             // array like object with random key ordering
             var anObj = { 100: 'a', 2: 'b', 7: 'c' };
             console.log(Object.keys(anObj)); // console: ['2', '7', '100']
      

Object.preventExtensions()方法让一个对象变的不可扩展,也就是永远不能再添加新的属性

hasOwnProperty() 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是是否有指定的键)

  • 使用 hasOwnProperty 方法判断属性是否存在

          o = new Object();
          o.prop = 'exists';
    
          function changeO() {
            o.newprop = o.prop;
            delete o.prop;
          }
    
          o.hasOwnProperty('prop');   // 返回 true
          changeO();
          o.hasOwnProperty('prop');   // 返回 false
    
  • 自身属性与继承属性

          o = new Object();
          o.prop = 'exists';
          o.hasOwnProperty('prop');             // 返回 true
          o.hasOwnProperty('toString');         // 返回 false   继承属性
          o.hasOwnProperty('hasOwnProperty');   // 返回 false  继承属性
    

isPrototypeOf() 方法用于测试一个对象是否存在于另一个对象的原型链上。

Object.prototype.toString() 方法返回一个表示该对象的字符串。

toString() 返回 "[object type]"

  • 使用 toString() 检测对象类型
> 可以通过 toString() 来获取每个对象的类型。为了每个对象都能通过 Object.prototype.toString() 来检测,需要以 Function.prototype.call() 或者 Function.prototype.apply() 的形式来调用,传递要检查的对象作为第一个参数,称为 thisArg。

        var toString = Object.prototype.toString;

        toString.call(new Date); // [object Date]
        toString.call(new String); // [object String]
        toString.call(Math); // [object Math]

        //Since JavaScript 1.8.5
        toString.call(undefined); // [object Undefined]
        toString.call(null); // [object Null]

Error

  • URIError 对象用来表示以一种错误的方式使用全局URI处理函数而产生的错误。

  • TypeError(类型错误) 对象用来表示值的类型非预期类型时发生的错误。

  • SyntaxError 对象代表尝试解析语法上不合法的代码的错误

  • ReferenceError(引用错误) 对象代表当一个不存在的变量被引用时发生的错误。

  • RangeError对象标明一个错误,当一个值不在其所允许的范围或者集合中。

  • InternalError 对象表示出现在JavaScript引擎内部的错误。 例如: "InternalError: too much recursion"(内部错误:递归过深

  • EvalError eval 函数的错误.此异常不再会被JavaScript抛出,但是EvalError对象仍然保持兼容性.


eval() 函数会将传入的字符串当做 JavaScript 代码进行执行。

URL

decodeURI()

函数解码一个由encodeURI 先前创建的统一资源标识符(URI)或类似的例程。

    decodeURI("https://developer.mozilla.org/ru/docs/JavaScript_%D1%88%D0%B5%D0%BB%D0%BB%D1%8B");
    // "https://developer.mozilla.org/ru/docs/JavaScript_шеллы"

decodeURIComponent() :解码

方法用于解码由 encodeURIComponent 方法或者其它类似方法编码的部分统一资源标识符(URI)

    decodeURIComponent("JavaScript_%D1%88%D0%B5%D0%BB%D0%BB%D1%8B");
    // "JavaScript_шеллы"

encodeURI()

函数通过将特定字符的每个实例替换为一个、两个、三或四转义序列来对统一资源标识符 (URI) 进行编码

encodeURIComponent():编码

是对统一资源标识符(URI)的组成部分进行编码的方法

es5严格模式 规则

  • 使用var声明变量严格模式中将不通过

  • 何使用'eval'的操作都会被禁止

  • eval作用域

  • with被禁用

  • caller/callee 被禁用

  • 禁止扩展的对象添加新属性会报错

  • 除系统内置的属性会报错

  • delete使用var声明的变量或挂在window上的变量报错

  • delete不可删除属性(isSealed或isFrozen)的对象时报错

  • 对一个对象的只读属性进行赋值将报错

  • 对象有重名的属性将报错

  • 函数有重名的参数将报错

  • 八进制表示法被禁用

  • arguments严格定义为参数,不再与形参绑定

  • 函数必须声明在顶层

  • ES5里新增的关键字不能当做变量标示符使用,如implements, interface, let, package, private, protected, pulic, static, yield

  • call/apply的第一个参数直接传入不包装为对象

  • call/apply的第一个参数为null/undefined时,this为null/undefined

  • bind的第一个参数为null/undefined时,this为null/undefined

ES6

String

前言:只写觉得有用的

  • 实例方法:includes(), startsWith(), endsWith()----可以用来确定一个字符串是否包含在另一个字符串中。ES6 又提供了三种新方法。
  • 实例方法:repeat()-----返回一个新字符串,表示将原字符串重复n次
  • 实例方法:padStart(),padEnd()----如果某个字符串不够指定长度,会在头部或尾部补全
  • 实例方法:trim(),trimStart(),trimEnd()----删除前后多余空格
  • 实例方法:matchAll()---正则

repeat():原字符串重复n次

repeat方法返回一个新字符串,表示将原字符串重复n次。

	'x'.repeat(3) // "xxx"
	'na'.repeat(2.9) // "nana"

padStart(),padEnd():指定字符补齐指定长度

如果某个字符串不够指定长度,会在头部或尾部补全。padStart()用于头部补全,padEnd()用于尾部补全。

	'x'.padStart(4, 'ab') // 'abax'  在x前面的

	'x'.padEnd(5, 'ab') // 'xabab'

--(如果原字符串的长度,等于或大于最大长度,则字符串补全不生效,返回原字符串。)

	'xxx'.padStart(2, 'ab') // 'xxx'
	'xxx'.padEnd(2, 'ab') // 'xxx'

trim():消除字符串的空格

消除字符串的空格--trim()[ES2019 对字符串实例新增了trimStart()和trimEnd()]

Number

前言:只写觉得有用的

实例方法:isFinite()--数值是否为有限 实例方法:.isInteger()--数值是否为整数

isFinite():是否有限

Number.isFinite()用来检查一个数值是否为有限的(finite),即不是Infinity。【Number.isNaN()用来检查一个值是否为NaN。】

isInteger():是否整数

ES6 将全局方法parseInt()和parseFloat(),移植到Number对象上面,行为完全保持不变。【是逐步减少全局性方法,使得语言逐步模块化。】

    // ES5的写法
    parseInt('12.34') // 12
    parseFloat('123.45#') // 123.45

    // ES6的写法
    Number.parseInt('12.34') // 12
    Number.parseFloat('123.45#') // 123.45  

Number.isInteger()用来判断一个数值是否为整数。

    Number.isInteger(25) // true
    Number.isInteger(25.1) // false

Math 对象的扩展

前言:只写觉得有用的

实例方法:Math.trunc--去除一个数的小数部分,返回整数部分 实例方法:Math.sign--判断一个数到底是正数、负数、还是零 实例方法:Math.cbrt:计算一个数的立方根

Math.trunc:去除一个数的小数部分,返回整数部分

Math.trunc方法用于去除一个数的小数部分,返回整数部分。

    Math.trunc(4.1) // 4
    Math.trunc(4.9) // 4

Math.sign:判断一个数到底是正数、负数、还是零

Math.sign方法用来判断一个数到底是正数、负数、还是零。对于非数值,会先将其转换为数值。

参数为正数,返回+1;
参数为负数,返回-1;
参数为 0,返回0;
参数为-0,返回-0;
其他值,返回NaN。

Math.cbrt:计算一个数的立方根

Math.cbrt方法用于计算一个数的立方根。 Math.cbrt('8') // 2

Array

前言:扩展运算符...,将一个数组转为用逗号分隔的参数序列,有点for的意思。

扩展运算符...

  • 1.数组叠加

                  //items变成序列加到array数组里
                      function push(array, ...items) {
                          array.push(...items);
                      }
    
  • 2.扩展运算符与正常的函数参数可以结合使用,非常灵活。

                 //“...”,数组变成参数
                 function add(x, y) {
                   return x + y;
                 }
    
                 const numbers = [4, 38];
                 add(...numbers) // 42-----add(4,38)
    
  • 3.扩展运算符后面还可以放置表达式

            const arr = [
                ...(x > 0 ? ['a'] : []),
                    'b',
                ];
    
  • 4.扩展运算符后面是一个空数组,则不产生任何效果

          [...[], 1]
           // [1]
    
  • 5.函数调用时,扩展运算符才可以放在圆括号中,否则会报错

    (...[1, 2]) // Uncaught SyntaxError: Unexpected number

    console.log((...[1, 2])) // Uncaught SyntaxError: Unexpected number

    -----扩展运算符取代apply方法

    // ES6 的写法 Math.max(...[14, 3, 77])

    // 等同于 Math.max(14, 3, 77);

    -------通过push函数,将一个数组添加到另一个数组的尾部。

    // ES6 的写法 let arr1 = [0, 1, 2]; let arr2 = [3, 4, 5]; arr1.push(...arr2);

  • 扩展运算符的应用

  • (1) 复制数组

      //a2并不是a1的克隆,而是指向同一份数据的另一个指针。修改a2,会直接导致a1的变化
              [
                  const a1 = [1, 2];
                  const a2 = a1;
              ]
    
      //扩展运算符提供了复制数组的简便写法【修改a2,不会直接导致a1的变化】
              const a1 = [1, 2];
              // 写法一
              const a2 = [...a1];
              // 写法二
              const [...a2] = a1;
    
  • (2)合并数组

      const arr1 = ['a', 'b'];
      const arr2 = ['c'];
      const arr3 = ['d', 'e'];
    
      // ES5 的合并数组
      arr1.concat(arr2, arr3);
      // [ 'a', 'b', 'c', 'd', 'e' ]
    
      // ES6 的合并数组
      [...arr1, ...arr2, ...arr3]
      // [ 'a', 'b', 'c', 'd', 'e' ]
    
  • (3)解构赋值 tip:将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错

      const [first, ...rest] = [1, 2, 3, 4, 5];
      first // 1
      rest  // [2, 3, 4, 5]
    
      const [...butLast, last] = [1, 2, 3, 4, 5];
      // 报错
    
  • (4)字符串 tip:扩展运算符还可以将字符串转为真正的数组。

      [...'hello']
      // [ "h", "e", "l", "l", "o" ]
    
  • (5)实现了 Iterator 接口的对象 tip:querySelectorAll方法返回的是一个NodeList对象。 它不是数组,而是一个类似数组的对象。 这时,扩展运算符可以将其转为真正的数组。

      let nodeList = document.querySelectorAll('div');
      let array = [...nodeList];
    

Array.from():对象转为真正的数组

tip:方法用于将两类对象转为真正的数组: 类似数组的对象(array-like object)和可遍历(iterable)的对象【(包括 ES6 新增的数据结构 Set 和 Map).】

  • Array.from方法还支持类似数组的对象。
    • 所谓类似数组的对象,本质特征只有一点,即必须有length属性。

    • 因此,任何有length属性的对象,都可以通过Array.from方法转为数组.

          let arrayLike = {
              '0': 'a',
              '1': 'b',
              '2': 'c',
              length: 3
          };
          let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
      

Array.of方法用于将一组值,转换为数组。

Array.of(3, 11, 8) // [3,11,8]
Array.of(3) // [3]
Array.of(3).length // 1

copyWithin():指定位置的成员复制到其他位置

tips:将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组

  • target(必需):从该位置开始替换数据。如果为负值,表示倒数。

  • start(可选):从该位置开始读取数据,默认为 0。如果为负值,表示倒数。

  • end(可选):到该位置前停止读取数据,默认等于数组长度。如果为负值,表示倒数。

         [1, 2, 3, 4, 5].copyWithin(0, 3)
         // [4, 5, 3, 4, 5]  三个参数都是数组里读取
    

find() 和 findIndex():数组找符合条件的和返回位置

  • find() 找出第一个符合条件的数组成员。【只找一个】

    • 它的参数是一个回调函数,所有数组成员依次执行该回调函数,
    • 直到找出第一个返回值为true的成员,然后返回该成员。
    • 如果没有符合条件的成员,则返回undefined.
  • findIndex() 方法的用法与find方法非常类似,返回第一个符合条件的数组成员的位置,

    • 如果所有成员都不符合条件,则返回-1。

可以接受三个参数,依次为当前的值、当前的位置和原数组。

fill():使用给定值,填充一个数组

fill方法使用给定值,填充一个数组

        ['a', 'b', 'c'].fill(7, 1, 2)
        // ['a', 7, 'c']

includes():某个数组是否包含给定的值

方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes方法类似

        [1, 2, 3].includes(2)     // true
        [1, 2, 3].includes(4)     // false


        该方法的第二个参数表示搜索的起始位置
        [1, 2, 3].includes(3, 3);  // false

flat()与flatMap():扁平化数组与对每一个成员操作(扁平化)

flat()嵌套的数组“拉平”,变成一维的数组。该方法返回一个新数组

    [1, 2, [3, 4]].flat()
    // [1, 2, 3, 4]

不管有多少层嵌套,都要转成一维数组,可以用Infinity关键字作为参数,有空位,flat()方法会跳过空位。

    [1, [2, [3]]].flat(Infinity)
    // [1, 2, 3]

flatMap()方法对原数组的每个成员执行一个函数(相当于执行Array.prototype.map()), 然后对返回值组成的数组执行flat()方法。该方法返回一个新数组,不改变原数组。

        [2, 3, 4].flatMap((x) => [x, x * 2])
        // [2, 4, 3, 6, 4, 8]

Object

前言:ES6 对它进行了重大升级,本章介绍数据结构本身的改变;

属性的简洁表示法

        const foo = 'bar';
        const baz = {foo};
        baz // {foo: "bar"}

        // 等同于
        const baz = {foo: foo};

方法的 name 属性

        const person = {
            sayName() {
                console.log('hello!');
            },
        };

        person.sayName.name   // "sayName"

super 关键字

this关键字总是指向函数所在的当前对象,ES6 又新增了另一个类似的关键字super,指向当前对象的原型对象

        const proto = {
          foo: 'hello'
        };

        const obj = {
          foo: 'world',
          find() {
            return super.foo;
          }
        };

        Object.setPrototypeOf(obj, proto);
        obj.find() // "hello"

新增方法

Object.is():两个值是否严格相等

比较两个值是否严格相等Object.is()

+0 === -0 //true
NaN === NaN // false

Object.is(+0, -0) // false
Object.is(NaN, NaN) // true

Object.assign():对象的合并等用处

用于对象的合并Object.assign()

  • (1)目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。

  • (2)如果该参数不是对象,则会先转成对象,然后返回。

  • (3)由于undefined和null无法转成对象,所以如果它们作为参数,就会报错。-----不在首参数,就不会报错

  • (4)除了字符串会以数组形式,拷贝入目标对象,其他值都不会产生效果

  • (5)Object.assign方法实行的是浅拷贝-----对象的任何变化,都会反映到目标对象上面

       const target = { a: 1 };
    
       const source1 = { b: 2 };
       const source2 = { c: 3 };
    
       Object.assign(target, source1, source2);
       target // {a:1, b:2, c:3}
    
       const obj = {a: 1};
       Object.assign(obj) === obj // true
    
  • Object.assign方法用处很多

    • (1)为对象添加属性

      class Point { constructor(x, y) { Object.assign(this, {x, y});//等同于 this.x=x;this.y=y } }

    • (2)为对象添加方法

        Object.assign(SomeClass.prototype, {
                        someMethod(arg1, arg2) {
                                    ···
                        },
                        anotherMethod() {
                                    ···
                             }
                        });
      
    • (3)克隆对象--克隆它继承的值。如果想要保持继承链

        function clone(origin) {
                    let originProto = Object.getPrototypeOf(origin);
                    return Object.assign(Object.create(originProto), origin);
        }
      
    • (4)合并多个对象

        const merge =
                    (...sources) => Object.assign({}, ...sources);
      
    • (5)为属性指定默认值

Object.getOwnPropertyDescriptors():返回某个对象属性的描述对象

返回某个对象属性的描述对象(descriptor)------Object.getOwnPropertyDescriptors()----引入目的:Object.assign()无法正确拷贝get属性和set属性的问题。

Object.setPrototypeOf():获取设置原型对象

Object.setPrototypeOf(),Object.getPrototypeOf()获取设置原型对象

{constructor: ƒ, defineGetter: ƒ, defineSetter: ƒ, hasOwnProperty: ƒ, lookupGetter: ƒ, …}

Object.keys:对象键值返回数组

Object.keys方法,返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键名

            var obj = { foo: 'bar', baz: 42 };
            Object.keys(obj)
            // ["foo", "baz"]

Object.values:对象属性的键值返回数组

Object.values方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值。

            const obj = { foo: 'bar', baz: 42 };
            Object.values(obj)
            // ["bar", 42]

Object.entries():对象键值对转成键值对数组

Object.entries()方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组

                const obj = { foo: 'bar', baz: 42 };
                Object.entries(obj)
                // [ ["foo", "bar"], ["baz", 42] ]

Object.fromEntries():键值对数组转为对象

Object.fromEntries()---方法是Object.entries()的逆操作,用于将一个键值对数组转为对象。

                    Object.fromEntries([
                      ['foo', 'bar'],
                      ['baz', 42]
                    ])
                    // { foo: "bar", baz: 42 }

Test

<script>
        const modle = "一切皆对象";
        let alerts = ["请你回答Yes Or No", { "selset": "Yes", "Yes": "在弱类型语言JavaScript面前的确有时候一切皆对象" }, { "selset": "No", "No": "同志你还没学透彻啊!" }];
        let sheet=["Object.assign",{"info":"目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性"},{"info":"如果该参数不是对象,则会先转成对象,然后返回"},{"info":"由于undefined和null无法转成对象,所以如果它们作为参数,就会报错。-----不在首参数,就不会报错"},{"info":"除了字符串会以数组形式,拷贝入目标对象,其他值都不会产生效果"},{"info":"Object.assign方法实行的是浅拷贝-----对象的任何变化,都会反映到目标对象上面"}]
        var name = prompt(modle);
        for (let i = 0; i < alerts.length; i++) {
            if (Object.is(i, 0)) {
                if (Object.is(name, alerts[i + 1]["selset"]) || Object.is(name, alerts[i + 2]["selset"])) {
                    Object.is(name, alerts[i + 1]["selset"]) ? alert(alerts[i + 1][alerts[i + 1]["selset"]]) : alert(alerts[i + 1][alerts[i + 2]["selset"]]);
                } else {
                    name = window.confirm(alerts[i]);
                    if (name) {
                        alert("你很棒帮哦")
                    }else{
                        alert("你个菜鸡");
                    }
                }
            }else{
                Object.assign(alerts,sheet);
                alert(alerts[i]["info"]);
            }
        }
    </script>

Symbol

前言: ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。它是 JavaScript 语言的第七种数据类型

注意:Symbol函数前不能使用new命令,否则会报错。 let s = Symbol();

这是因为生成的 Symbol 是一个原始类型的值,不是对象。 也就是说,由于 Symbol 值不是对象,所以不能添加属性 。 typeof s 基本上,它是一种类似于字符串的数据类型。 // "symbol"

参数相同,但是它们是不相等的

        // 没有参数的情况
        let s1 = Symbol();
        let s2 = Symbol();

        s1 === s2 // false

        // 有参数的情况
        let s1 = Symbol('foo');
        let s2 = Symbol('foo');

        s1 === s2 // false

Symbol 值不能与其他类型的值进行运算,会报错

        let sym = Symbol('My symbol');

        "your symbol is " + sym
        // TypeError: can't convert symbol to string

Symbol 值可以显式转为字符串

        String(sym) // 'Symbol(My symbol)'
        sym.toString() // 'Symbol(My symbol)'

ES2019 提供了一个实例属性description,直接返回 Symbol 的描述。

        symbol.description // "My symbol"

作为属性名的 Symbol

  • 写法

       let mySymbol = Symbol();
    
       // 第一种写法
       let a = {};
       a[mySymbol] = 'Hello!';
    
       // 第二种写法
       let a = {
         [mySymbol]: 'Hello!'
       };
    
       // 第三种写法
       let a = {};
       Object.defineProperty(a, mySymbol, { value: 'Hello!' });
    
       // 以上写法都得到同样结果
       a[mySymbol] // "Hello!"
    
  • 2.风格良好的代码,应该尽量消除魔术字符串,改由含义清晰的变量代替。

              const shapeType = {
                triangle: 'Triangle'
              };//变量
    
              function getArea(shape, options) {
                let area = 0;
                switch (shape) {
                  case shapeType.triangle:
                    area = .5 * options.width * options.height;
                    break;
                }
                return area;
              }
    
              getArea(shapeType.triangle, { width: 100, height: 100 });
    

ES6的Symbol竟然那么强大,面试中的加分点啊

Set

Set前言:新的数据结构 Set,它类似于数组,但是成员的值都是唯一的,没有重复的值。

  • Set本身是一个构造函数,用来生成 Set 数据结构。

     例1:const s = new Set();
         [2, 3, 5, 4, 5, 2, 2].forEach(x=>s.add(x));
    
     例2:const set = new Set([1, 2, 3, 4, 4]);
          [...set]
     例3:[...new Set('ababbc')].join('')//abc
    
    1. 向 Set 加入值的时候,不会发生类型转换,所以5和"5"是两个不同的值,两个NaN是相等,两个空对象不相等.
    1. Set.prototype.constructor:构造函数,默认就是Set函数。 Set.prototype.size:返回Set实例的成员总数。

操作方法:

add(value):添加某个值,返回 Set 结构本身。

delete(value):删除某个值,返回一个布尔值,表示删除是否成功。

has(value):返回一个布尔值,表示该值是否为Set的成员。

clear():清除所有成员,没有返回值。

                const s = new Set();
    1.---       s.add(1);s.add(2);s.add(2);
                // 注意2被加入了两次
                s.size // 2

    2.---       s.has(1) // true
                s.has(2) // true
                s.has(3) // false

    3.---       s.delete(2);
                 s.has(2) // false

遍历方法:

keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历

keys():返回键名的遍历器

values():返回键值的遍历器

entries():返回键值对的遍历器

forEach():使用回调函数遍历每个成员

  • 1.keys方法和values方法的行为完全一致

     let set = new Set(['red', 'green', 'blue']);
    
     for (let item of set.keys()) {
       console.log(item);
     }
     // red
     // green
     // blue
    
     for (let item of set.values()) {
       console.log(item);
     }
     // red
     // green
     // blue
    
  • 2.entries方法返回的遍历器,同时包括键名和键值,所以每次输出一个数组,它的两个成员完全相等。

     for (let item of set.entries()) {
       console.log(item);
     }
     // ["red", "red"]
     // ["green", "green"]
     // ["blue", "blue"]
    
  • 3.省略values方法,直接用for...of循环遍历 Set。

     let set = new Set(['red', 'green', 'blue']);
    
     for (let x of set) {
       console.log(x);
     }
     // red
     // green
     // blue
    
  • 4.forEach方法的参数就是一个处理函数。该函数的参数与数组的forEach一致,依次为键值、键名、集合本身

     let set = new Set([1, 4, 9]);
     set.forEach((value, key) => console.log(key + ' : ' + value))
     // 1 : 1
     // 4 : 4
     // 9 : 9
    

遍历的应用

+ 1. 扩展运算符(...)内部使用for...of循环

            let set = new Set(['red', 'green', 'blue']);
            let arr = [...set];
            // ['red', 'green', 'blue']
+ 2. 扩展运算符和 Set 结构相结合,就可以去除数组的重复成员

            let arr = [3, 5, 2, 2, 5, 5];
            let unique = [...new Set(arr)];// [3, 5, 2]

Map

类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键

  • tips:Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。

     const map = new Map([
         ['name', '张三'],
         ['title', 'Author']
       ]);
    
       map.size // 2
       map.has('name') // true
       map.get('name') // "张三"
       map.has('title') // true
       map.get('title') // "Author"
    

解释:执行的算法是

      const items = [
      ['name', '张三'],
      ['title', 'Author']
    ];

    const map = new Map();

    items.forEach(
      ([key, value]) => map.set(key, value)
    );
  • 实例的属性和操作方法

    • (1)size 属性

      size属性返回 Map 结构的成员总数。
      
      const map = new Map();
      map.set('foo', true);
      map.set('bar', false);
      
      map.size // 2
      
    • (2)set(key, value)

      set方法设置键名key对应的键值为value,然后返回整个 Map 结构。如果key已经有值,则键值会被更新,否则就新生成该键。

      const m = new Map();

      m.set('edition', 6) // 键是字符串 m.set(262, 'standard') // 键是数值 m.set(undefined, 'nah') // 键是 undefined

    • (3)get()

      方法读取key对应的键值,如果找不到key,返回undefined。

            const m = new Map();
      
            const hello = function() {console.log('hello');};
            m.set(hello, 'Hello ES6!') // 键是函数
      
            m.get(hello)  // Hello ES6!
      
    • (4)has\delete\clear

ES6 完全使用手册

ES6 系列之 defineProperty 与 proxy

ES6核心特性

近一万字的ES6语法知识点补充

理解 ES6 generator

模块化开发

AMD, CMD, CommonJS和UMD

关于前端模块化 CommonJS、AMD、CMD、ES6中模块加载