一名【合格】前端工程师的自检清单 - 答案版(原型和原型链)

话很少说,直接开始正题吧.今天就是JavaScript基础篇第二部分 - 原型和原型链javascript

原文地址: 一名【合格】前端工程师的自检清单前端

1.理解原型设计模式以及JavaScript中的原型规则

  • 原型模式:

是指原型实例指向建立对象的种类,并经过拷贝这些原型建立新的对象,是一种用来建立对象的模式,也就是建立一个对象做为另外一个对象的prototype属性。java

  • 原型规则:
  1. 全部的引用类型(数组、对象、函数),都具备对象特征,便可自由扩展属性;
  2. 全部的引用类型,都有一个_proto_属性(隐式原型),属性值是一个普通对象;
  3. 全部函数,都具备一个prototype(显示原型),属性值也是一个普通原型;
  4. 全部的引用类型(数组、对象、函数),其隐式原型指向其构造函数的显式原型;(obj.proto === Object.prototype)
  5. 当试图获得一个对象的某个属性时,若是这个对象自己没有这个属性,那么会去它的_proto_(即它的构造函数的prototype)中去寻找;

2.instanceof的底层实现原理,手动实现一个instanceof

instanceof的底层实现原理:es6

我我的理解是从当前引用的proto一层一层顺着原型链往上找,可否找到对应的prototype。找到了就返回true设计模式

手动实现: 如下这段代码应该是最基础的instanceof的实现代码了数组

function instance_of(L, R) {//L 表示左表达式,R 表示右表达式 
    var O = R.prototype;   // 取 R 的显示原型 
    L = L.__proto__;  // 取 L 的隐式原型
    while (true) {    
    	if (L === null)      
    	    return false;   
    	if (O === L)  // 当 O 显式原型 严格等于 L隐式原型 时,返回true
    	    return true;   
    	L = L.__proto__;  
    }
}
复制代码

3.实现继承的几种方式以及他们的优缺点

1. 原型链继承:

Cat.prototype = new Animal();浏览器

特色:babel

  • 很是纯粹的继承关系,实例是子类的实例,也是父类的实例
  • 父类新增原型方法/原型属性,子类都能访问到
  • 简单,易于实现

缺点:前端工程师

  • 要想为子类新增属性和方法,必需要在new Animal()这样的语句以后执行,不能放到构造器中
  • 没法实现多继承
  • 来自原型对象的引用属性是全部实例共享的
  • 建立子类实例时,没法向父类构造函数传参

2. 构造继承:

function Cat(name){
    Animal.call(this); 
    this.name = name || 'Tom';
}
复制代码

特色:闭包

  • 解决了原型链继承中,子类实例共享父类引用属性的问题
  • 建立子类实例时,能够向父类传递参数
  • 能够实现多继承(call多个父类对象)

缺点:

  • 实例并非父类的实例,只是子类的实例
  • 只能继承父类的实例属性和方法,不能继承原型属性/方法
  • 没法实现函数复用,每一个子类都有父类实例函数的副本,影响性能

3. 实例继承

function Cat(name){ 
    var instance = new Animal();
    instance.name = name || 'Tom'; 
    return instance;
}
复制代码

特色:

  • 不限制调用方式,无论是new 子类()仍是子类(),返回的对象具备相同的效果

缺点:

  • 实例是父类的实例,不是子类的实例
  • 不支持多继承

4. 拷贝继承

function Cat(name){ 
    var animal = new Animal(); 
    for(var p in animal){ 
    	Cat.prototype[p] = animal[p]; 
    } 
    Cat.prototype.name = name || 'Tom';
}
复制代码

特色:

  • 支持多继承

缺点:

  • 效率较低,内存占用高(由于要拷贝父类的属性)
  • 没法获取父类不可枚举的方法(不可枚举方法,不能使用for in访问到)

5. 组合继承

function Cat(name){
    Animal.call(this); 
    this.name = name || 'Tom'; 
} 
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
复制代码

特色:

  • 弥补了方式2的缺陷,能够继承实例属性/方法,也能够继承原型属性/方法
  • 既是子类的实例,也是父类的实例
  • 不存在引用属性共享问题
  • 可传参
  • 函数可复用

缺点:

  • 调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)

6. 寄生继承

function Cat(name){ 
    Animal.call(this); 
    this.name = name || 'Tom';
}

(function(){ 
    // 建立一个没有实例方法的类
    var Super = function(){};
    Super.prototype = Animal.prototype;
    //将实例做为子类的原型
    Cat.prototype = new Super();
})();

Cat.prototype.constructor = Cat;
复制代码

特色:

  • 堪称完美

缺点:

  • 实现较为复杂

7. Class继承

class Cat extends Animals {}

特色:

  • 堪称完美

缺点:

  • es6语法糖,须要必定的浏览器兼容性或者polyfill

4.至少说出一种开源项目(如Node)中应用原型继承的案例

这个案例就不一一列举了,感兴趣的同窗能够自行查阅开源项目源码.

5.能够描述new一个对象的详细过程,手动实现一个new操做符

如下答案转载自MDN - new 运算符:

new 运算符建立一个用户定义的对象类型的实例或具备构造函数的内置对象的实例。new 关键字会进行以下的操做:

  1. 建立一个空的简单JavaScript对象(即{});
  2. 连接该对象(即设置该对象的构造函数)到另外一个对象 ;
  3. 将步骤1新建立的对象做为this的上下文 ;
  4. 若是该函数没有返回对象,则返回this。
//本身定义的new方法
let newMethod = function (Parent, ...rest) {
    // 1.以构造器的prototype属性为原型,建立新对象;
    let child = Object.create(Parent.prototype);
    // 2.将this和调用参数传给构造器执行 
    Parent.apply(child, rest);
    // 3.返回第一步的对象
    return child;
};
复制代码

6.理解es6 class构造以及继承的底层实现原理

ES6类的底层仍是经过构造函数以及原型链继承实现,具体实现能够参考babel编译成的es5代码实现.

系列连接:

相关文章
相关标签/搜索