JavaScript

  1. js数据类型以及如何判断数据类型

    基本数据类型: string number boolean null undefined symbol
    引用数据类型:object
    判断数据类型常用的是typeof、instanceof(可以准确的判断对象的类型)

  2. 作用域和作用域链

    含义:定义变量的区域,有一套访问变量的规则,根据这规则来管理浏览器引擎如何在当前作用域和嵌套作用域根据变量进行变量查找
    作用域的分类:全局作用域、函数作用域、块作用域
    作用域链: 当js使用一个变量的时候,会先在当前作用域寻找该变量,如何当前作用域找不到,则回到上一级作用域去寻找,以此类推知道找到该变量

  3. this

    在全局范围内, this指向的是window
    函数中this指的是调用他的那个对象(箭头函数特殊)
    构造函数中,this指向的是new出来的新对象
    call, apply中,this被绑定在指定的对象
    箭头函数中,this指向的是函数定义时作用域的this

  4. 原型与原型链

    4.1 构造函数
    ES的构造函数其实就是能创建对象的函数,使用new调用的函数是构造函数,直接调用的则是普通函数

    1
    2
    3
    4
    5
    6
    7
    8
    function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function() { alert(this.name) }
    }
    var person1 = new Person('Zaxlct', 28, 'Software Engineer');
    var person2 = new Person('Mick', 23, 'Doctor');

    person1和person2都是person的实例,这两个实例都有一个constructor(即构造函数),该属性指向Person,换句话说就是实例的构造函数属性指向构造函数
    其实person1和person2也是Object的实例,因为所有自定义对象都是继承于Object

    constructor其实是用来标识对象类型的,但实际上一般使用instanceof来确定对象类型,所以如下表达式成立

    1
    2
    3
    4
    console.log(person1.constructor == Person); //true
    console.log(person2.constructor == Person); //true
    console.log(person1 instanceof Object); // true
    console.log(person1 instanceof Person) // true

    4.2 原型对象
    js中每定义一个对象(函数也是对象)时,对象中都会包含一些预先定义的属性,当定义函数对象时,就会被包含prototype属性,该属性指向函数的原型对象
    Object.getPrototypeOf() 可以获得传入对象的原型对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    function Person() {}
    Person.prototype.name = 'Zaxlct';
    Person.prototype.age = 28;
    Person.prototype.job = 'Software Engineer';
    Person.prototype.sayName = function() {
    alert(this.name);
    }

    var person1 = new Person();
    person1.sayName(); // 'Zaxlct'

    var person2 = new Person();
    person2.sayName(); // 'Zaxlct'

    console.log(person1.sayName == person2.sayName); //true

    每个对象都有__proto__属性,只有函数对象才有prototype属性, 但是Function.prototype除外
    那什么是原型对象呢?看如下代码(Person.prototype指向原型对象)

    1
    2
    3
    4
    5
    6
    7
    8
    Person.prototype = {
    name: 'Zaxlct',
    age: 28,
    job: 'Software Engineer',
    sayName: function() {
    alert(this.name);
    }
    }

    原型对象其实就是一个普通对象,其实就是Person.prototype,甚至可以给其赋值 var a = Person.prototype,上述代码中给a添加四个属性name、age、job、sayName,但其实还有一个默认属性construcor

    • 默认情况下,所有原型对象都会有一个构造函数(constructor)属性,这个属性指向prototype所在的函数(Person),并且这个元素是个指针,即 Person.prototype.constructor == Person

    • 从之前的结论可以知道实例的构造函数指向构造函数, 即 person1.constructor == Person

    • 换句话说,Person.prototype也是Person的实例(有待考证),即原型对象是构造函数的一个实例

    • 实例只有指向原型的指针,没有指向构造函数的指针

    • 重写构造函数的原型之后,在创建的实例才会引用新的原型

      4.3 proto
      js在创建对象的时候都有一个叫做proto的内置属性,用于指向创建他的构造函数的原型对象

      person1.proto== Person.prototype

      4.4 构造器
      我们可以使用 var obj = {}来创建一个对象,等同于var obj = new Object(),所以

      obj.constructor == Object
      obj.proto == Object.prototype

      同理,创建对象的构造器不只是Object,也可以是Array,Date,Function等
      这些构造器都是函数对象

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      var b = new Array();
      b.constructor === Array;
      b.__proto__ === Array.prototype;

      var c = new Date();
      c.constructor === Date;
      c.__proto__ === Date.prototype;

      var d = new Function();
      d.constructor === Function;
      d.__proto__ === Function.prototype;

      所有函数对象的_proto_都指向Function.prototype,它是一个空函数(Empty function)
      所有对象的_proto_都指向其构造器的prototype
      4.5 原型链

    • person1.proto = Person.prototype

      person1的_proto_等于person1构造函数的prototype
      person1.constructor == Person

    • Person.proto = Function.ptototype

      Person._proto_等于Person.constructor的prototype,
      Person.constructor = Function

    • Person.prototype.proto = Object.prototype

      Person.prototype是一个普通对象,即Object.prototype

    • Object.proto = Function.prototype

      object的构造函数是Function

    • Object.prototype.proto = null

      Object.prototype 对象也有proto属性,为null,因为他处于原型链的顶端

      4.6 函数对象
      所有函数对象的_proto_(即所有的构造器)都指向Function.prototype,换句话说所有函数对象的构造函数都是Function
      所有构造器都继承Function.prototype的属性及方法

      Function.prototype是唯一一个 typeof Function.prototype == Function的prototype
      其他构造器的prototype都是object
      普通对象.proto = Function.ptototype
      Function.prototype.proto === Object.prototype
      Object.prototype.proto === null

      4.7 原型链案例

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      function Person() {}
      var person1 = new Person()
      person1.__proto__ === Person.prototype
      Person.__proto__ == Function.prototype // 所有函数对象的__proto__都指向Function.prototype
      Person.prototype._proto_ == Object.prototype

      Function.__proto__ == Function.prototype // Function也属于函数对象
      Function.prototype.__proto__ == Object.prototype
      Object.prototype.__proto__== null


      var num = new Array()
      num.__proto__ == Array.prototype
      Array.prototype.__proto__ == Object.prototype
      Object.prototype.__proto__ == null
      Array.__proto__ = Function.prototype

      4.8 hasOwnProperty、in、for in的用法

      hasOwnProperty() 方法用于确定某个属性是否存在于实例中,存在则返回true
      in 可以确定某属性是否存在于原型或实例上,只要存在就返回true
      因此只要hasOwnProperty()返回false, in 返回true,就说明该属性是原型属性
      在 for-in 循环中使用 in 操作符时,可以通过对象访问且可以被枚举的属性都会返回,包括实例属性和原型属性
      获得对象上所有可枚举的实例属性,使用Object.keys(obj)
      列出所有实例属性(包含不可枚举的)可使用Object.getOwnPropertyNames()

      4.9 ES2017新增Object.values()和Object.entries()

      Object.value()返回对象值的数组
      Object.entries()返回对象键值对的数组

      1
      2
      3
      4
      5
      6
      7
      8
      const o = {
      foo: 'bar'
      ,
      baz: 1,
      qux: {}
      };
      console.log(Object.values(o)); // ["bar", 1, {}]
      console.log(Object.entries((o))); // [["foo","bar"], ["baz", 1], ["qux", {}]]

      4.10 原型链的问题

    • 原型中包含的引用值会在所有实例间共享

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      function SuperType() {
      this.colors = ["red" , "blue" , "green"];
      }
      function SubType() {}
      // 继承SuperType
      SubType.prototype = new SuperType();
      let instance1 = new SubType();
      instance1.colors.push("black");
      console.log(instance1.colors); // "red,blue,green,black"
      let instance2 = new SubType();
      console.log(instance2.colors); // "red,blue,green,black"
    • 子类型在实例化时不能给父类型的构造函数传参

      4.11 盗用构造函数
      为解决原型包含引用值导致的继承问题,“盗用构造函数”流行了起来,也叫“对象伪装”,“经典继承”
      基本思路:在子类构造函数中通过call/apply调用父类构造函数

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      function SuperType() {
      this.colors = ["red","blue","green"];
      }
      function SubType() {
      // 继承SuperType
      SuperType.call(this);
      }
      let instance1 = new SubType();
      instance1.colors.push("black");
      console.log(instance1.colors); // "red,blue,green,black"
      let instance2 = new SubType();
      console.log(instance2.colors); // "red,blue,green"

      优点:在子类构造函数中向父类构造函数传参
      存在的问题:必须在构造函数中定义方法,因此函数不能重用
      4.12 new做了什么

      • 创建了一个全新的对象
      • 该对象会被执行[prototype]链接
      • 生成的新对象会绑定到函数调用的this
      • 通过new创建的对象最终都会被[prototype]链接到该函数的原型(也就是prototype)对象上
      • 如果函数没有返回对象Object(包含Function、Array等),那么new表达式中的函数调用会自动返回这个新函数

评论