JS原型详解

原型

  原型是 JavaScript 面向对象特性中重要的概念,也是大家太熟悉的概念。因为在绝大多
数的面向对象语言中,对象是基于类的(例如 Java 和 C++ ),对象是类实例化的结果。而在
JavaScript 语言中,没有类的概念①,对象由对象实例化。打个比方来说,基于类的语言中类
就像一个模具,对象由这个模具浇注产生,而基于原型的语言中,原型就好像是一件艺术品
的原件,我们通过一台 100% 精确的机器把这个原件复制出很多份。
 让我们看看如何使用原型和构造函数共同生成对象。

function Person() {
}
Person.prototype.name = 'BYVoid';
Person.prototype.showName = function () {
console.log(this.name);
};
var person = new Person();
person.showName();

  上面这段代码使用了原型而不是构造函数初始化对象。这样做与直接在构造函数内定义
属性有什么不同呢?
1. 构造函数内定义的属性继承方式与原型不同,子对象需要显式调用父对象才能继承构 造函数内定义的属性。
2. 构造函数内定义的任何属性,包括函数在内都会被重复创建,同一个构造函数产生的 两个对象不共享实例。
3. 构造函数内定义的函数有运行时闭包的开销,因为构造函数内的局部变量对其中定义 的函数来说也是可见的。

下面这段代码可以验证以上问题:

function Foo() {
var innerVar = 'hello';
this.prop1 = 'BYVoid';
this.func1 = function(){
innerVar = '';
};
}
Foo.prototype.prop2 = 'Carbo';
Foo.prototype.func2 = function () {
console.log(this.prop2);
};
var foo1 = new Foo();
var foo2 = new Foo();
console.log(foo1.func1 == foo2.func1); // 输出 false
console.log(foo1.func2 == foo2.func2); // 输出 true

尽管如此,并不是说在构造函数内创建属性不好,而是两者各有适合的范围。那么我们
什么时候使用原型,什么时候使用构造函数内定义来创建属性呢?

  1. 除非必须用构造函数闭包,否则尽量用原型定义成员函数,因为这样可以减少开销。
  2. 尽量在构造函数内定义一般成员,尤其是对象或数组,因为用原型定义的成员是多个 实例共享的。

接下来,我们介绍一下JavaScript中的原型链机制。


原型链

  JavaScript 中有两个特殊的对象: Object 与 Function,它们都是构造函数,用于生
成对象。 Object.prototype 是所有对象的祖先, Function.prototype 是所有函数的原
型,包括构造函数。我把 JavaScript 中的对象分为三类,一类是用户创建的对象,一类是**构
造函数对象,一类是原型对象**。用户创建的对象,即一般意义上用 new 语句显式构造的对
象。构造函数对象指的是普通的构造函数,即通过 new 调用生成普通对象的函数。原型对象
特指构造函数 prototype 属性指向的对象。这三类对象中每一类都有一个 proto
性,它指向该对象的原型,从任何对象沿着它开始遍历都可以追溯到 Object.prototype。
构造函数对象有 prototype 属性,指向一个原型对象,通过该构造函数创建对象时,被创
建对象的 proto 属性将会指向构造函数的 prototype 属性。原型对象有 constructor
属性,指向它对应的构造函数。让我们通过下面这个例子来理解原型:

function Foo() {
}
Object.prototype.name = 'My Object';
Foo.prototype.name = 'Bar';
var obj = new Object();
var foo = new Foo();
console.log(obj.name); // 输出 My Object
console.log(foo.name); // 输出 Bar
console.log(foo.__proto__.name); // 输出 Bar
console.log(foo.__proto__.__proto__.name); // 输出 My Object
console.log(foo. __proto__.constructor.prototype.name); // 输出 Bar

  我们定义了一个叫做 Foo ()的构造函数,生成了对象 foo。同时我们还分别给 Object
和 Foo 生成原型对象。
   图A-1 解析了它们之间错综复杂的关系
   js原型间的关系
                     图 A-1 JavaScript 原型之间的关系

  在 JavaScript 中,继承是依靠一套叫做原型链(prototype chain)的机制实现的。属性
继承的本质就是一个对象可以访问到它的原型链上任何一个原型对象的属性。例如上例的
foo 对象,它拥有 foo. _ _ proto_ _ 和 foo. _ _ proto_ _ . _ _ proto _ _ 所有属性的浅拷
贝(只复制基本数据类型,不复制对象)。所以可以直接访问foo.constructor(来自foo.
_ _ proto_ _ ,即Foo.prototype), foo.toString(来自foo. _ _ proto_ _ . _ _ proto__,
即Object.prototype)。
这里写图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值