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;

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值