JavaScript 原型链

君子生非异也,善假于物也。
——《荀子·劝学》

JavaScript 中关于类的继承是一个原型继承的方式,总结起来其精髓就在于“”这个字。

JavaScript 中,对于一个对象,它所能访问的属性,不一定全是它自己的属性,也有可能是它原型的属性,甚至是原型的原型的属性……

__proto__ 是一个属性,除了nullundefined 没有以外,其他的都具有这个属性。用于指向对象的原型,是一个引用(指针)。

"123".__proto__ === String.prototype; // true
(1).__proto__ === Number.prototype; // true
(function A(x){
     return x;}).__proto__ === Function.prototype; // true

constructor 与 prototype

对象的构造器(constructor) 是一个函数。
函数的原型是一个原型对象(prototype)。

从一个函数(类)能构造出很多对象,但一个函数只有对应的一个原型。从某种意义上来说,这个原型对象也是函数的实例之一,不过这是一个特殊的实例。

假设有一个函数 A

function A(){
     };

对于其原型(prototype)对象obj来说,构造器就是A

var obj = A.prototype;
obj.constructor === A; // true

但对于一般的f实例化对象x

var x = new A();
x.hasOwnProperty('constructor'); // false
x.constructor === A; // true

x 是不具有constructor 属性的,但x.constructor 依然能访问,并且指向的也是A。这是因为x.constructor 沿着原型链进行了回溯搜索,实际上x.constructor 就是x.__proto__.constructor,而x.__proto__ 就是A.prototype

构造过程

function A(){
     this.key = 1;};
A.prototype.val = 1;
var x = new A();

对于这样一个基本的构造。我们可以发现,x 可访问的属性如下

x.__defineGetter__
x.__defineSetter__
x.__lookupGetter__
x.__lookupSetter__
x.__proto__
x.constructor
x.hasOwnProperty
x.isPrototypeOf
x.propertyIsEnumerable
x.toLocaleString
x.toString
x.valueOf
x.val
x.key

实际上真正属于 x 自己的属性有什么呢?
只有x.key 是真正属于x 的,这两个都是在new A() 的调用过程中产生的。
其他的,就连x.val 都是属于A.prototype 而不属于x

x.__proto__ === A.prototype; // true
x.__proto__.__proto__ === Object.prototype; // true
x.__proto__.__proto__.__proto__ === null; // true

x.hasOwnProperty('__proto__'); // false
x.__proto__.hasOwnProperty('__proto__'); // false
x.__proto__.__proto__.hasOwnProperty('__proto__'); // true

实际上我们可以看到,x 能访问的属性,除了__proto__, key 这样自身的属性之外,还有A.prototype, Object.prototype 的。

总结:那么现在我们可以知道在构造过程究竟发生了什么:

  1. 构造对象
  2. 原型链接
  3. 应用构造函数
function myNew(f) {
     
    // create new object
    var obj = {};
    // link to prototype chain
    obj.__proto__ = f.prototype;
    // apply constructor to the object
    f.apply(obj, Array.prototype.slice.call(arguments, 1)); 
    // return the object
    return obj;
}

这个过程也可以手动地操作一下。

function f() {
      this.key = 1; };
f.prototype.val = 2;
var x = {};
x instanceof f; // false
x.val; // undefined
x.key; // undefined

x.__proto__ = f.prototype;
x instanceof f; // true
x.val; // 2
x.key; // undefined

f.call(x); // same as f.apply(x, []);
x instanceof f; // true
x.val; // 2
x.key; // 1

原型继承

类的继承其实很简单:

function A(){
     };
function B(){
     };
// B inherits A
B.prototype.__proto__ = A.prototype;

这样就完成了类的继承,如果理解了最终的实例对象的寻找属性的方式,那么这种做法是显而易见的。

你可能感兴趣的:(JavaScript,javascript,继承,原型链)