一、原型
1、JavaScript 中,函数(function)是允许拥有属性的。所有的函数会有一个特别的属性 —— prototype 。
2、每个实例对象( object )都有一个私有属性(称之为 proto )指向它的构造函数的原型对象(prototype )。
3、该原型对象也有一个自己的原型对象( proto ) ,层层向上直到一个对象的原型对象为 null。根据定义,null 没有原型,并作为这个原型链中的最后一个环节。
4、几乎所有 JavaScript 中的对象都是位于原型链顶端的 Object 的实例。
5、原型中有一个constructor 它指向函数对象
6、给原型添加属性和方法,函数所有的实例对象都能访问。
function doSomething(){}
console.log( doSomething.prototype );
// 和声明函数的方式无关,
// JavaScript 中的函数永远有一个默认原型属性。
var doSomething = function(){};
console.log( doSomething.prototype );
在控制台显示的JavaScript代码块中,我们可以看到doSomething函数的一个默认属性prototype。而这段代码运行之后,控制台应该显示类似如下的结果:
{
constructor: ƒ doSomething(),
__proto__: {
constructor: ƒ Object(),
hasOwnProperty: ƒ hasOwnProperty(),
isPrototypeOf: ƒ isPrototypeOf(),
propertyIsEnumerable: ƒ propertyIsEnumerable(),
toLocaleString: ƒ toLocaleString(),
toString: ƒ toString(),
valueOf: ƒ valueOf()
}
}
我们可以给doSomething函数的原型对象添加新属性,如下:
function doSomething(){}
doSomething.prototype.foo = "bar";
console.log( doSomething.prototype );
可以看到运行后的结果如下:
{
foo: "bar",
constructor: ƒ doSomething(),
__proto__: {
constructor: ƒ Object(),
hasOwnProperty: ƒ hasOwnProperty(),
isPrototypeOf: ƒ isPrototypeOf(),
propertyIsEnumerable: ƒ propertyIsEnumerable(),
toLocaleString: ƒ toLocaleString(),
toString: ƒ toString(),
valueOf: ƒ valueOf()
}
}
现在我们可以通过new操作符来创建基于这个原型对象的doSomething实例。使用new操作符,只需在调用doSomething函数语句之前添加new。这样,便可以获得这个函数的一个实例对象。一些属性就可以添加到该原型对象中。
function doSomething(){}
doSomething.prototype.foo = "bar"; // add a property onto the prototype
var doSomeInstancing = new doSomething();
doSomeInstancing.prop = "some value"; // add a property onto the object
console.log( doSomeInstancing );
如果 doSomeInstancing 不包含属性信息, 那么浏览器会在 doSomeInstancing 的 proto 中进行查找(同 doSomething.prototype). 如属性在 doSomeInstancing 的 proto 中查找到,则使用 doSomeInstancing 中 proto 的属性。
否则,如果 doSomeInstancing 中 proto 不具有该属性,则检查doSomeInstancing 的 proto 的 proto 是否具有该属性。默认情况下,任何函数的原型属性 proto 都是 window.Object.prototype. 因此, 通过doSomeInstancing 的 proto 的 proto ( 同 doSomething.prototype 的 proto (同 Object.prototype)) 来查找要搜索的属性。
如果属性不存在 doSomeInstancing 的 proto 的 proto 中, 那么就会在doSomeInstancing 的 proto 的 proto 的 proto 中查找。然而, 这里存在个问题:doSomeInstancing 的 proto 的 proto 的 proto 其实不存在。因此,只有这样,在 proto 的整个原型链被查看之后,这里没有更多的 proto , 浏览器断言该属性不存在,并给出属性值为 undefined 的结论。
二、隐式原型和显式原型
1、每个函数function 都有一个prototype 即显示原型
2、每个实例对象都有一个__proto__,即隐式原型
3、实例对象的隐式原型的值为对应构造函数显示原型的值
4、函数的prototype 属性:在定义函数时自动添加,默认为一个空的Object 对象
5、对象的__proto__属性:创建对象时自动添加,默认值为构造函数的prototype属性值
6、可以直接操作显示原型 但不能直接操作隐式原型(ES6之前)
创建函数Fn 在定义时自动添加prototype 属性,创建Fn 函数实例对象fn 在创建时自动添加__proto__ 属性,Fn.prototype===fn.proto 成立 ,使用内存堆栈标示如图
三、原型链
1、定义
1、访问一个对象时,首先在自身属性中查找,找到返回,如果没有找到,再沿着__proto__ 这条链向上查找,找到返回。如果最终没有找到,侧返回undefined
(1)函数的显示元指向的对象默认的空Object实例对象
(2)所有的函数都是Function的实例(包含Function)
(3)Object的原型对象时原型链的尽头
2、原型链属性
1、读取对象属性时会自动到原型链中查询
2、设置对象的属性值时不会查询原型链,如果当前对象没有此属性,直接添加此属性并设置其值
3、方法一般定义在原型中,属性一般通过构造函数定义在对象本身上
3、使用不同的方法来创建对象和生成原型链
(1)使用语法结构创建的对象
var o = {a: 1};
// o 这个对象继承了 Object.prototype 上面的所有属性
// o 自身没有名为 hasOwnProperty 的属性
// hasOwnProperty 是 Object.prototype 的属性
// 因此 o 继承了 Object.prototype 的 hasOwnProperty
// Object.prototype 的原型为 null
// 原型链如下:
// o ---> Object.prototype ---> null
var a = ["yo", "whadup", "?"];
// 数组都继承于 Array.prototype
// (Array.prototype 中包含 indexOf, forEach 等方法)
// 原型链如下:
// a ---> Array.prototype ---> Object.prototype ---> null
function f(){
return 2;
}
// 函数都继承于 Function.prototype
// (Function.prototype 中包含 call, bind等方法)
// 原型链如下:
// f ---> Function.prototype ---> Object.prototype ---> null
(2)使用构造器创建的对象
在 JavaScript 中,构造器其实就是一个普通的函数。当使用 new 操作符 来作用这个函数时,它就可以被称为构造方法(构造函数)。
function Graph() {
this.vertices = [];
this.edges = [];
}
Graph.prototype = {
addVertex: function(v){
this.vertices.push(v);
}
};
var g = new Graph();
// g 是生成的对象,他的自身属性有 'vertices' 和 'edges'。
// 在 g 被实例化时,g.[[Prototype]] 指向了 Graph.prototype。
(3)使用 Object.create 创建的对象
ECMAScript 5 中引入了一个新方法:Object.create()。可以调用这个方法来创建一个新对象。新对象的原型就是调用 create 方法时传入的第一个参数:
var a = {a: 1};
// a ---> Object.prototype ---> null
var b = Object.create(a);
// b ---> a ---> Object.prototype ---> null
console.log(b.a); // 1 (继承而来)
var c = Object.create(b);
// c ---> b ---> a ---> Object.prototype ---> null
var d = Object.create(null);
// d ---> null
console.log(d.hasOwnProperty); // undefined, 因为d没有继承Object.prototype
(4) 使用 class 关键字创建的对象
ECMAScript6 引入了一套新的关键字用来实现 class。使用基于类语言的开发人员会对这些结构感到熟悉,但它们是不同的。JavaScript 仍然基于原型。这些新的关键字包括 class, constructor,static,extends 和 super。
"use strict";
class Polygon {
constructor(height, width) {
this.height = height;
this.width = width;
}
}
class Square extends Polygon {
constructor(sideLength) {
super(sideLength, sideLength);
}
get area() {
return this.height * this.width;
}
set sideLength(newLength) {
this.height = newLength;
this.width = newLength;
}
}
var square = new Square(2);