什么是JavaScript原型

JS 原型

原型是JavaScript最重要的概念。同时也是初级开发者最忌惮的内容,原因在于网上很少有关于它的合理描述。

但事实上,原型很简单,你可以很轻松的掌握它的知识要点。

什么是原型

了解什么是原型之前,我们先看一个示例:

var obj = {};
obj.toString(); // "[object Object]"

上面的例子中,我们声明了一个空对象,并没有为它添加toString属性方法,但这个方法却可以被成功调用并输出。是不是觉得很神奇?

原因在于JavaScript的对象都有一个内置的[[Prototype]]私有属性,这个属性指向另一个对象,我们称这个对象为原对象的原型。当JS引擎访问objtoString属性时,首先会去obj对象查找,发现找不到,就沿着obj[prototype]属性去他的原型上查找。

通过Chrome开发者工具,我们可以看到这个obj原型的真面目:

JavaScript空对象的原型

图中我们可以看到,obj的原型对象已经定义了一个toString属性。所以,空对象也可以使用toString()这个属性方法。

为什么需要原型

原型意义在于实现属性的继承

想象一下:编写一个JS脚本,创建1000个数组实例。如果在每个数组实例中单独定义数组的操作方法,不仅代码冗余,还会内存资源极大的浪费。有了原型,我们只需要把数组的操作方法定义在数组的原型上即可,实现了属性的共享。

举个例子:

我们希望JS的数字类型能提供一个方法判断当前数字是否为奇数,没有原型的情况下,你只能这么做:

var num = new Number(99);
num.isOdd = function(){return this % 100 !== 0};

num.isOdd(); // true

但是这样是没有意义的,因为isOdd()方法定义在num变量上面,其他数字类型并不能使用它:

var num2 = 100;
num2.isOdd(); // num2.isOdd is not a function

有了原型概念以后,由于所有数字类型都指向了同一个原型,我们可以把isOdd方法定义在这个原型上,这样,所有数字类型就都能调用到这个方法了:

var num1 = 99, num2 = 100, num3 = 0;
Number.prototype.isOdd = function(){return this % 100 !== 0};

num1.isOdd(); // true
num2.isOdd(); // false
num3.isOdd(); // false
学习后面的内容,你将明白:Number.prototype指向数字类型的原型

原型链

原型并不是一个特别的存在,它也只是一个普通的对象而已。

换句话说,原型也可以拥有属于它的原型。如果把对象的[[prototype]]属性想象成链条,就形成了一条原型链

接下来,我们通过一个示例来看下JS引擎是如何通过原型链查找属性的:

现在创建三个对象,并通过Object.setPrototypeOf()方法将它们连结成一条原型链:

var son = {a: 1, b: 2},
    parent = {b: 3, c: 4},
    ancestor = {d: 5};

Object.setPrototypeOf(son, parent);
Object.setPrototypeOf(parent, ancestor);

son.a // 1
son.b // 2
son.c // 4
son.d // 5

这三个对象形成将形成一条原型链,JS引擎将从左往右有序地查找目标属性:

JS在原型链中查找属性

如何设置和修改对象的原型

JavaScript分别通过 Object.setPrototypeOf()Object.getPrototypeOf() 两个方法来设置和获取对象的原型。

var parent = {type: 'parent'}, son = {type: 'son'};
Object.setPrototypeOf(son, parent);
Object.getPrototypeOf(son) === parent // true

内置对象实例的原型

JavaScript提供了一些内置对象(构造函数),比如Object, String, Array, Boolean等等,它们提供了prototype属性,指向实例的原型。因此,可以简单地通过instance.constructor.prototype来获取

var obj = {}, str = '', arr = [], bl = true;

Object.getPrototypeOf(obj) === obj.constructor.prototype // true
Object.getPrototypeOf(str) === str.constructor.prototype // true
Object.getPrototypeOf(arr) === arr.constructor.prototype // true
Object.getPrototypeOf(bl) === bl.constructor.prototype // true
如果你不理解constructor这个属性,可以阅读构造函数一节。

通过instance.constructor.prototype这种方式获取原型的方式并不是绝对可靠的。因为实例的constructor属性是可改变的(mutable)。一旦属性,instance.constructor.prototype便无法正确指向实例的原型。

var obj = new Object();
obj.constructor = Array;
Object.getPrototypeOf(obj) === obj.constructor.prototype // false

上面的例子中,我们随意修改了obj的constructor属性,然后obj.constructor.prototype便不再指向obj的原型了。

另外,对于自定义构造函数而言,其constructor也是可变的(内置构造函数的constructor被配置为不可改变)

综合来说,我们推荐使用 Object.getPrototypeOf() 方法获取实例原型。

转载至【EC前端 - 原型

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值