原型和原型链

178 阅读5分钟
  • 理解原型设计模式以及JavaScript中的原型规则:
    1.原型规则:
     (1)所有的引用类型(数组、对象、函数),都具有对象特征,即可自由扩展属性
     (2)所有的引用类型,都有一个_proto_ 属性(隐式原型),属性值是一个普通对象;
     (3)所有函数,都具有一个prototype(显示原型),属性值也是一个普通原型;
     (4)所有的引用类型(数组、对象、函数),其隐式原型指向其构造函数的显式原型;(obj.proto === Object.prototype)
     (5)当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的_proto_(即它的构造函数的prototype)中去寻找;
    2.原型对象
    prototype 在js中,函数对象其中一个属性:原型对象prototype。普通对象没有prototype属性,但有_proto_属性。 原型的作用就是给这个类的每一个对象都添加一个统一的方法,在原型中定义的方法和属性都是被所以实例对象所共享。

3.原型链
 当试图得到一个对象f的某个属性时,如果这个对象本身没有这个属性,那么会去它的_proto_(即它的构造函数的prototype )obj._proto_中去寻找;当obj._proto也没有时,便会在obj._proto.proto(即obj的构造函数的prototype的构造函数的prototype)中寻找;
4.设计模式
(1)工厂模式(在函数内创建一个对象,给对象赋予属性及方法再将对象返回)
(2)构造函数模式(无需在函数内部重新创建对象,而是用this指代)
(3)原型模式(函数中不对属性进行定义,利用prototype属性对属性进行定义,可以让所有对象实例共享它所包含的属性及方法。)
(4)混合模式:原型模式+构造函数模式。这种模式中,构造函数模式用于定义实例属性,而原型模式用于定义方法和共享属性
(5)动态原型模式:将所有信息封装在了构造函数中,而通过构造函数中初始化原型,这个可以通过判断该方法是否有效而选择是否需要初始化原型。

  • instanceof的底层实现原理,手动实现一个instanceof
    1.JavaScript instanceof 语言规范 (简化版) 的运算代码如下:(规则简单来说就是 L的 proto 是不是强等于 R.prototype,不等于再找 L.proto .proto 直到 proto 为 null )
    2.JavaScript 原型链,看下图:
    2.1.function Foo 就是一个方法,比如内置的 Array ,String ,或者自定义的方法;
    2.2.function Object 就是Object
    2.3.function Function 就是Function
    2.4以上三个其实都是function 所以他们的_proto_ 都是Function.prototype
    2.5.记住 String, Array, Number, Object, Function这些其实都是 function
    for example:
  • 实现继承的几种方式以及他们的优缺点
    理解原型链:每一个实例对象都有一个__proto__属性(隐式原型),在js内部用来查找原型链;每一个构造函数都有prototype属性(显示原型),用来显示修改对象的原型,实例.proto=构造函数.prototype=原型。
    原型链的特点就是:通过实例.__proto__查找原型上的属性,从子类一直向上查找对象原型的属性,继而形成一个查找链即原型链。
    1.原型链继承:
    我们使用原型继承时,主要利用sub.prototype=new super,这样连通了子类-子类原型-父类。
    缺点:构造函数原型上的属性在所有该构造函数构造的实例上是共享的,即属性没有私有化,原型上属性的改变会作用到所有的实例上。
    2.构造函数继承:
    在构造子类构造函数时内部使用call或apply来调用父类的构造函数
    优缺点:实现了属性的私有化,但是子类无法访问父类原型上的属性。
    3.组合继承
    利用构造函数和原型链的方法,可以比较完美的实现继承

这里还有个小问题,Sub.prototype = new Super; 会导致Sub.prototype的constructor指向Super;然而constructor的定义是要指向原型属性对应的构造函数的,Sub.prototype是Sub构造函数的原型,所以应该添加一句纠正:Sub.prototype.constructor = Sub;
4.寄生继承
即将sub.prototype=new super改为sub.prototype=Object.creat(supper.prototype),避免了组合继承中构造函数调用了两次的弊端。

  • 可以描述new一个对象的详细过程,手动实现一个new操作符
    对比一下,发现p的__proto__的值就是构造函数Person的prototype的属性值。
    so:
    1.创建一个空对象
    2.将所创建对象的__proto__属性值设为构造函数的prototype的属性值
    3.执行构造函数中的代码,构造函数中的this指向该对象
    4.返回对象
    For example:
  • 至少说出一种开源项目(如Node)中应用原型继承的案例
  • 理解es6 class构造以及继承的底层实现原理:
    es6 class 使用:javascript使用的是原型式继承,我们可以通过原型的特性实现类的继承, es6为我们提供了像面向对象继承一样的语法糖。

1.类的实现:
1.1调用_classCallCheck方法判断当前函数调用前是否有new关键字。
1.2将class内部的变量和函数赋给this。
1.3执行constuctor内部的逻辑
1.4.return this (构造函数默认在最后我们做了)。
2.继承实现
2.1.调用_inherits函数继承父类的proptype。
inherits内部实现: (1) 校验父构造函数。
(2) 典型的寄生继承:用父类构造函数的proptype创建一个空对象,并将这个对象指向子类构造函数的proptype。
(3) 将父构造函数指向子构造函数的_proto
(这步是做什么的不太明确,感觉没什么意义。)
2.2用一个闭包保存父类引用,在闭包内部做子类构造逻辑。
2.3.new检查。
2.4.用当前this调用父类构造函数。
2.5.将行子类class内部的变量和函数赋给this。
2.6.执行子类constuctor内部的逻辑。
3. super super代表父类构造函数。
super.fun1() 等同于 Parent.fun1() 或 Parent.prototype.fun1()。
super() 等同于Parent.prototype.construtor()