[学习笔记] JavaScript 原型

今天研究了一下js的原型,把本身的理解写到这里,若有不正确的地方,还望指出,在此先谢过啦~javascript

什么是原型?

原型是一个对象。全部对象都有原型。任何一个对象也均可以成为其余对象的原型。
每一个原型都有一个 constructor 属性指向其构造函数。html

怎么访问原型?

一个对象的原型被对象内部的 [[Prototype]] 属性所持有。
一句话就是,全部对象都有原型,其原型是该对象的内部属性。那么有哪些方法能够访问到这个内部属性呢?java

  • ECMAScript 5 增长了方法 Object.getPrototypeOf() ,用于返回对象 [[Prototype]] 的值。IE 9+、Firefox 3.5+、Safari 5+、Opera 12+ 和 Chrome 都实现了该方法。
  • 除了IE,其余浏览器都支持非标准的访问器 __proto__
  • 若是这二者都不起做用,咱们就须要经过对象的构造函数找它的原型属性了。
var a = {};
Object.getPrototypeOf(a);
a.__proto__;
a.constructor.prototype;

函数的 prototype 属性

不知亲刚刚有没有注意到访问 [[Prototype]] 的最后一个方法 a.constructor.prototype; ,直接使用了构造函数的 prototype属性。git

在这里要多说两句,js中函数也是对象,因此函数也有原型,其原型也和其余对象同样,能够经过 Object.getPrototypeOf()__proto__ 访问。可是建立对象时,咱们每每要使用构造函数 (constructor) 的原型,为了用起来方便,就给构造函数添加了 prototype 属性,用于直接访问其原型。因为全部的函数均可以成为构造函数,因此就造就了函数有那么一点点特(you)殊(shi)—— 一个函数能够经过其 prototype 属性直接访问其原型,或者说 一个函数的 prototype 属性指向其原型对象浏览器

若是仅仅只是由于一个实例而使用原型是没有多大意义的,这和直接添加属性到这个实例是同样的。原型真正魅力体如今多个实例共用一个原型,原型对象的属性一旦定义,就能够被多个引用它的实例所继承,这种操做在性能和维护方面其意义是不言自明的。闭包

这也是构造函数存在的缘由,构造函数提供了一种方便的跨浏览器机制,这种机制容许在建立实例时为实例提供一个通用的原型。函数

原型语法

在使用原型前,先写一下构造函数部分。性能

function Calculator (decimalDigits, tax) {
    this.decimalDigits = decimalDigits;
    this.tax = tax;
};

分步声明

分开设置原型的每一个属性。this

Calculator.prototype.add = function (x, y) {
    return x + y;
};

Calculator.prototype.subtract = function (x, y) {
    return x - y;
};

这样,在new Calculator对象之后,就能够调用add方法来计算结果了。
此时, Calculator.prototypeconstructor 属性指向 Calculator。即:
Calculator.prototype.constructor = Calculator.net

更简单的原型语法

经过给Calculator的prototype属性赋值对象字面量来设定Calculator对象的原型。

Calculator.prototype = {
    add: function (x, y) {
        return x + y;
    },

    subtract: function (x, y) {
        return x - y;
    }
};

上面的代码中,将 Calculator.prototype 赋值为一个以对象字面量形式建立的新对象。最终结果相同,可是,此时 constructor 属性再也不指向 Calculator ,即 Calculator.prototype.constructor != Calculator

每建立一个函数,就会同时建立它的 prototype 对象,这个对象会自动得到 constructor 属性。而咱们这里使用的语法,本质上彻底重写了默认的 prototype 对象,所以 constructor 属性也就变成了新对象的 constructor 属性 —— 指向 Object 构造函数。

若是 constructor 的值很重要,能够向下面同样设定:

Calculator.prototype = {

    constructor: Calculator, // 修复constructor 属性

    add: function (x, y) {
        return x + y;
    },

    subtract: function (x, y) {
        return x - y;
    }
};

其余方式

使用function当即执行的表达式来为prototype赋值,格式以下:

Calculator.prototype = function () {
    var add = function (x, y) {
        return x + y;
    },

    var subtract = function (x, y) {
        return x - y;
    }

    return {
        add: add,
        subtract: subtract
    }
} ();

原型的动态性

var friend = new Person();

Person.prototype.sayHi = function () {
    alert("hi");
};

friend.sayHi(); // "hi" (没有问题)

在以上代码中,即便 friend 是在添加新方法以前建立的,但它仍然能够访问这个新方法。其缘由能够归结为实例与原型之间的松散链接关系。当咱们调用 friend.sayHi() 时,会首先在实例中搜索名为 sayHi 的属性,在没有找到的状况下,会继续搜索原型。由于实例实例与原型之间链接是一个指针,而非副本,所以能够在原型中找到新的 sayHi 属性并返回保存在那里的函数。

能够随时为原型添加属性和方法,而且修改能当即在全部对象实例中反映出来,但若是重写了原型对象,那结果就不同了……

function Person () {
}

var friend = new Person();

Person.prototype = {
    constructor: Person,
    name: "Nicholas",
    age: 29,
    job: "software Engineer",
    sayHi: function () {
        alert("hi");
    }
};

friend.sayHi(); // error

在这个例子中,先建立了 friend 实例,而后又重写其原型对象。在调用 friend.sayHi() 时发生错误,由于friend 指向的原型中不包含以该名字命名的属性。

重写原型对象切断了现有原型与任何以前已经存在的对象实例之间的联系,这些实例仍然引用的是最初的原型。

一切都是对象

这部分与原型没什么大关系,只是看到了觉有帮助,就插到这里了,莫要见怪 :)

固然,也不是全部的都是对象,值类型就不是对象。

举个不太容易理解的例子,函数做为对象时,其属性仍是函数的例子。

var fn = function () {
    alert(100);
};

fn.a = 10;
fn.b = function () {
    alert(123);
};
fn.c = {
    name: "福布斯",
    year: 1968
};

上段代码中,函数就做为对象被赋值了a、b、c三个属性,第二个属性值就是函数,这个有用吗?
能够看看jQuery源码。

在jQuery源码中,“jQuery”或者“$”,这个变量实际上是一个函数,不信能够用 typeof 验证一下。

console.log(typeof $);  // function
console.log($.trim(" ABC "));

验明正身!的确是个函数。而常用的 $.trim() 也是个函数。

很明显,这就是在 $ 或者 jQuery 函数上加了一个 trim 属性,属性值是函数,做用是截取先后空格。要习惯的把js中的一切看做对象,只要是对象,就是属性的集合,属性是键值对的形式。

参考资料

相关文章
相关标签/搜索