Live
Meta

Zaibeiwo

百度ife-javascript原型学习笔记

2015-06-24 06:15

学习任务来自:百度 ife 前端技术学院

学习资料参考自:

JavaScript 探秘:强大的原型和原型链

理解 JavaScript 原型

深入理解 javascript 原型和闭包

个人注:主要是区分Prototype,prototype,__proto__,[[Prototype]],理解constructor

  • Prototype:原型本尊
  • prototype:原型属性,指向Person,实例中不存在
  • __proto__:原型访问器,指向 创造该对象 的原型
  • [[Prototype]]:等同于__proto__
  • constructor:构造函数,指向函数本身(如 new Person),constructor.prototype也指向Prototype

javascript 原型和原型链

对象中的proto属性(隐式原型,javascript 并不希望我们访问到该属性)

每当创建一个对象,该对象就会有一个__proto__属性,该属性指向创建该对象的函数的原型

如下代码中:

var foo = {
  x: 10,
  y: 20,
};

当我们创建了一个 foo 对象,foo 对象的__proto__属性就会指向 foo 的原型Prototype;同时 foo 的Prototype也是一个对象,它也有__proto__,指向Object.prototype;在同时,Object.prototype__proto__指向null。因此,可以说是个对象就有__proto__属性!

而且,对象是沿着__proto__这条原型链来走的!!!

constructor 属性

function Foo(y) {
  this.y = y;
}

Foo.prototype.x = 10;

Foo.prototype.calculate = function (z) {
  return this.x + this.y + z;
};

var b = new Foo(20);

alert(b.calculate(30));

此时,Foo.prototype 有一个constructor属性,指向 Foo 对象

原型

  • 原型是一个对象,其他对象可以通过它实现属性继承。
  • 任何一个对象都可以成为原型
  • 所有的对象在默认的情况下都有一个原型,因为原型本身也是对象,所以每个原型自身又有一个原型(只有一种例外,默认的对象原型在原型链的顶端。

一个对象的真正原型是被对象内部的[[Prototype]]属性(property)所持有。ECMA 引入了标准对象原型访问器 Object.getPrototype(object),到目前为止只有 Firefox 和 chrome 实现了此访问器。除了 IE,其他的浏览器支持非标准的访问器__proto__,如果这两者都不起作用的,我们需要从对象的构造函数中找到的它原型属性。下面的代码展示了获取对象原型的方法:

var a = {};

//Firefox 3.6 and Chrome 5
Object.getPrototypeOf(a); //[object Object]

//Firefox 3.6, Chrome 5 and Safari 4
a.__proto__; //[object Object]

//all browsers
a.constructor.prototype; //[object Object]

因此,不难理解,__proto__ 就是指向对象的原型,而不支持__proto__访问器的,constructor 的原型指向对象的原型

当试图用基本类型访问原型时,内部发生了强制转换:

//(works in IE too, but only by accident)

string.__proto__ === String().__proto__; //true

因此,基本类型没有原型的说法是正确的

instanceof 的工作原理

如下代码中:

function Foo() {}
var f1 = new Foo();

console.log(f1 instanceof Foo); // true
console.log(f1 instanceof Object); //true

在使用 instanceof 时,其内部的工作时这样的:

  • 沿着f1.__proto__这条链向上找
  • 沿着Foo.prototype这条链向上找
  • 当两条线找到同一个引用,返回 true,如果到终点还未重合,sorry,返回 false

hasOwnProperty 的由来

var Person = function () {
  this.name = "Jimmy";
};

Person.prototype.sayName = function () {
  alert(this.name);
};

var person = new Person();

person.sayHello = function () {
  alert("hello");
};

var item;
for (item in person) {
  // 如果不加 hasOwnProperty,会遍历出prototype上的sayName
  if (person.hasOwnProperty(item)) {
    console.log(item);
  }
}

如上所述的,person里本来是没有 hasOwnProperty 属性的,它其实是由 Object.prototype 继承而来的!

对象沿着__proto__这条原型链来查找!!!

person__proto__属性指向Person的原型,而Person的原型的__proto__属性又指向创建它的Object的原型,而Object的原型上就有这个方法~

原型继承

原型继承的好处

如果仅仅只是因为一个实例而使用原型是没有多大意义的,这和直接添加属性到这个实例是一样的。

原型真正魅力体现在多个实例共用一个通用原型的时候。原型对象(注:也就是某个对象的原型所引用的对象)的属性一旦定义,就可以被多个引用它的实例所继承(注:即这些实例对象的原型所指向的就是这个原型对象),这种操作在性能和维护方面其意义是不言自明的。

构造函数

构造函数提供了一种方便的跨浏览器机制,这种机制 允许在创建实例时为实例提供一个通用的原型

javascript 中构造函数(constructor)也是一个函数,因此也是一个对象,自然就有了原型属性(与原型区分开来)。

//function will never be a constructor but it has a prototype property anyway

Math.max.prototype; //[object Object]

//function intended to be a constructor has a prototype too
var A = function (name) {
  this.name = name;
};

A.prototype; //[object Object]

//Math is not a function so no prototype property
Math.prototype; //null

原型属性只存在于函数上,而不存在与实例上:

var A = function () {
  this.name = "Jimmy";
};

console.log(A.prototype); // A

var a = new A();

console.log(a.prototype); // undefined

函数 A 的原型属性(prototype property)是一个对象,当这个函数被用作构造函数来创建实例时,该函数的 原型属性 将被作为 原型 赋值给 所有对象实例 (注:即所有实例的原型引用的是函数的原型属性)

注意:一个函数的原型属性(function’s prototype property)其实和实际的原型(prototype)没有关系

//(example fails in IE)

var A = function (name) {
  this.name = name;
};

A.prototype == A.__proto__; //false

A.__proto__ == Function.prototype;
//true - A's prototype is set to its constructor's prototype property

如果我现在替换 A 的原型属性为一个新的对象,实例对象的原型a.__proto__却仍然引用着原来它被创建时 A 的原型属性

var A = function (name) {
  this.name = name;
};
var a = new A("alpha");
a.name; //'alpha'
A.prototype = { x: 23 };
a.x; //null

如果在实例被创建之后,改变了函数的原型属性所指向的对象,也就是改变了创建实例时实例原型所指向的对象

拓展已有的对象

拓展已有对象的方式本身是不推荐的,如果真的需要对内置对象的原型进行拓展,检测该属性是否存在应该是代码第一件要做的事情!

// 特性检测
if (String.prototype.timers) {
  String.prototype.times = function (count) {
    return count < 1 ? "" : new Array(count + 1).join(this);
  };
}

"hello!".times(3); // "hello!hello!hello!";

"please...".times(6); // "please...please...please...please...please...please..."

原型通过原型链来继承

原型的继承机制是发生在内部且是隐式的.当想要获得一个对象 a 的属性 foo 的值,javascript 会在原型链中查找 foo 的存在,如果找到则返回 foo 的值,否则 undefined 被返回。