《你不知道的JavaScript》 原型

1 [[Prototype]]

[[Prototype]]是对其他对象的引用,几乎所有对象在创建时[[Prototype]]属性会被赋予非空值。

var myObject = {
      a:2
}

myObject.a;  //  2  

引用对象属性时会触发[[Get]]操作,它会检查对象本身是否有这个属性,如果有就使用它,但a不在myObject,需要使用对象的[[Prototype]]链。

使用for in遍历对象时原理和查找[[Prototype]]链类似,任何可以通过原型链访问到的属性都会被枚举。使用in操作符来检查属性在对象是否存在也会查找对象的整条原型链。

var anotherObject = {
      a:2
}

var myObject = Object.create( anotherObject  );

for(var k in myObject){
    console.log( k );
}

1.1 Object.prototype

所有普通的[[Prototype]]链最终都指向内置的Object.prototype。由于所有的“普通”(内置,不少特定主机的扩展)对象都源于这个Object.prototype,所以它包含JavaScript许多通用功能。

1.2 属性设置和屏蔽

myObject.foo = "bar";

如果foo不是直接存在于myObject,[[Prototype]]链会被遍历,如果原型链找不到foo,foo会被添加到myObject。

如果foo不直接存在于myObject而是存在于原型链上层时:

  1. 如果在[[Prototype]]链上层存在名为foo的普通数据访问属性并且没有被标记为只读(writeable:false),直接在myObject添加foo属性,它是屏蔽属性
  2. 如果在[[Prototype]]链上存在foo,但它被标记为只读,将无法修改已有属性或在myObject上创建屏蔽属性。在严格模式中会抛出错误,否则忽略此语句。
  3. 如果在[[Prototype]]链上存在foo且它是一个setter,那一定会调用这个setter。foo不会被添加到myObject,也不会重新定义foo。

如果细微第二种和第三种情况也会屏蔽foo,不能使用=赋值,而是使用Object.defineProperty()向myObject添加foo。

var anotherObject = {
      a:2
}

var myObject = Object.create( anotherObject  );

anotherObject.a;  //  2
myObject.a;  //  2

anotherObject.hasOwnProperty("a");  // true
myObject.hasOwnProperty("a");  //  false

myObject.a++;  //隐式屏蔽

anotherObject.a;  //  2
myObject.a;  //  3

myObject.hasOwnProperty("a");  //  true

++操作相当于myObject.a = myObject.a + 1。++操作先会通过[[Prototype]]查找属性a并从anotherObject.a获取当前属性值2,然后给它加1,接着用[[Put]]将3赋值给myObejct的新建屏蔽属性a。

如果想让anotherObjecg的值增加,唯一的办法是anotherObject.a++。

2 "类"

2.1 "类"函数

function Foo() {}

Foo.prototype;  //  {}

这个对象在调用new Foo()时创建,最后被关联到“Foo点prototype”。

function foo() {}

var a = new foo();

foo.prototype == Object.getPrototypeOf(a)  //  true

调用new foo()创建a,其中一步是将a的[[Prototype]]连接到foo.prototype所指向的对象

2.2 “构造函数”

function Foo() {}

Foo.prototype.constructor === Foo;  // true

var a = new Foo();

a.constructor === Foo;  //  true

Foo.prototype默认有一个公有且不可枚举属性.constructor,它引用的是对象关联的函数(Foo)。通过“构造函数”调用new Foo()创建的对象也有一个.constructor属性,指向“创建这个对象的函数”。

当在普通的函数调用前面加上new后,就会把这个函数调用变成一个“构造函数调用”。new会劫持普通函数并用构造对象的形式聊调用它。

function NothingSpecial() {
    console.log("Don‘t mind me");
}

var a = new NothingSpecial();

a; // {}

NothingSpecial只是普通函数,但使用new调用时,它会构造一个对象并赋值给 a

在JavaScript对“构造函数”最准确的解释是,所有带new的函数调用。

2.3 技术

a.constructor === Foo为真,不代表a有指向Foo的.constructor属性。.constructor引用同样被委托给Foo.prototype,foo.prototype.constructor默认指向foo。a.constructor只是通过默认[[prototype]]委托指向Foo。

Foo.prototype的.constructor属性只是Foo函数在声明时的默认属性。如果你创建一个新对象并替换了函数默认的.prototype对象引用,新对象不会自动获得.constructor属性。

function Foo() { /*...*/ }

Foo.prototype = { /*...*/ }

var a1 = new Foo();

a1.constructor === Foo;  //  false
a1.constructor === true  //  true

a1没有.constructor属性,它会委托[[Prototype]]链上的Foo.prototype。但这个对象也没.constructor属性(已经被修改),所以它会继续委托,委托给了委托链顶端的Object.prototype。这个对象的.constructor属性指向内置的Object函数。

3 (原型)继承

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

Foo.prototype.myName = function() {
    return this.name;
}

function Bar(name, label) {
    Foo.call(this, name);
    this.label = label;
}

//创建新的Bar.prototype对象并关联到Foo.prototype
Bar.prototype = Object.create(Foo.prototype);

//现在没有Bar.prototype.constructor,需要手动修复
Bar.prototype.myLabel = function() {
    return this.label;
}

var a = new Bar("a", "obj a");

a.myName();  //  "a"
a.myLabel();  //  "obj a"

调用Object.create()会凭空创建一个“新”对象并把新对象内部的[[Prototype]]关联到你指定的对象(Foo.prototype)。

Bar.prototype = Object.create(Foo.prototype) 的意思是“创建一个新的Bar.prototype对象并把它关联到Foo.prototype”。

//错误做法

//  和想要的机制不一样
Bar.prototype = Foo.prototype;

//可能产生副作用
Bar.prototype = new Foo();

Bar.prototype = Foo.prototype 只是让Bar.prototype直接引用Foo.prototype对象。因此执行如Bar.prototype.mylabel = ...的赋值语句会直接修改Foo.prototype对象本身。

Bar.prototype = new Foo() 会创建关联到Bar.prototype的新对象,但它使用的是构造函数调用,如果函数Foo有一些副作用(如写日志、修改状态、注册到其他对象、给this添加数据属性等),就会影响到Bar()的“后代”。

检查“类关系

在传统面向类环境,检查一个实例(JavaScript的对象)的继承祖先(JavaScript的委托关系)通常被成为内省(或者反射)。

function Foo( ) {
    //  ...
}

Foo.prototype.blash = ...;

var a = new Foo();

第一种方法: a instanceof Foo ;  //  true

instanceof 判断a的整条[[Prototype]]链是否有指向 Foo.prototype的对象。但这个方法只能处理对象和函数的关系。

第二种方法: Foo.prototype.isPropertyOf( a );  //  true

4 对象关联

[[Prototype]]机制是存在于对象的一个内部链接,它会引用其他对象。通常它的作用是:如果在对象上没有找到需要的属性或方法引用,引擎就会继续在 [[Prototype]]关联的对象上查找。这一系列对象的链接被称为“原型链”。

4.1创建关联

var foo = {
    something: function() {
        console.log("Tell me someting goos.");
    }
}

var bar = Object.create( foo );

bar.something( );   //  Tell me someting goos.

Object.create会创建一个新对象并把它关联到指定的对象(foo)。

Object.create(null)会创建一个拥有空(或者说null)[[Prototype]]链接的对象,这个对象无法进行委托。因为他没有原型链,所以instanceof无法进行判断,总返回false。这些特殊的空[[Prototype]]对象通常被称为“字典”,它们完全不受原型链影响,非常适合存储数据

Object.create的polyfill代码

Object.create是ES5新增的函数,在ES5前的环境要支持这个功能要使用这段代码,它实现了Obejct.create的部分功能

if(! Object.create) {
    Object.create = function(o) {
        function F() {};
        F.prototype = o;
        return new F();
    }
}

这段代码使用了一个一次性函数F,通过改写它的.prototype使其指向想要关联的对象,然后再使用new F()构造一个新对象进行关联。Object.create的第二个参数指定需要添加到新对象的属性名和这些属性的属性描述符,但ES5之前的版本无法模拟属性描述符。

4.2 关联关系是必备的

假设要调用myObject.cool(),如果myObject中不存在cool()也能正常工作,这API会很“神奇”。

var anotherObject = {
    cool: function() {
        console.log("cool!");
    }
}

var myObject = Object.create( anotherObject );

myObject.cool( );   //  cool!
var anotherObject = {
    cool: function() {
        console.log("cool.");
    }
}

var myObject = Object.create( anotherObject );

myObject.doCool = function() {
    this.cool();  //  内部委托
}

myObject.doCool( );  

这里强调的myObject.doCool()是实际存在于myObject的,让API设计更加清晰。从内部来说,这种实现遵循的是委托设计模式,通过[[Prototype]]委托到anotherObject.cool()。

内部委托比起直接委托可以让API接口设计更加清晰。

时间: 2024-10-26 07:31:47

《你不知道的JavaScript》 原型的相关文章

JavaScript 原型与继承机制详解

引言 初识 JavaScript 对象的时候,我以为 JS 是没有继承这种说法的,虽说 JS 是一门面向对象语言,可是面向对象的一些特性在 JS 中并不存在(比如多态,不过严格来说也没有继承).这就困惑了我很长的时间,当我学习到 JS 原型的时候,我才发现了 JS 的新世界.本篇文章讲解了 JavaScript new 操作符与对象的关系.原型和对象关联(也就是俗称的继承)的原理,适合有一定基础的同学阅读. 一.JavaScript 的类与对象 许多书籍上都会说到如何在 JS 当中定义“类”,通

《你不知道的JavaScript》整理(二)——this

最近在读一本进阶的JavaScript的书<你不知道的JavaScript(上卷)>,这次研究了一下“this”. 当一个函数被调用时,会创建一个活动记录(执行上下文). 这个记录会包含函数在哪里被调用(调用栈).函数的调用方法.传入的参数等信息. this就是记录的其中一个属性,会在函数执行的过程中用到. this既不指向函数自身也不指向函数的作用域. this实际上是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用. 一.调用位置 调用位置就在当前正在执行的函数的前一个调用中

《你不知道的JavaScript》系列分享专栏

<你不知道的JavaScript>系列分享专栏 你不知道的JavaScript"系列就是要让不求甚解的JavaScript开发者迎难而上,深入语言内部,弄清楚JavaScript每一个零部件的用途 <你不知道的JavaScript>已整理成PDF文档,点击可直接下载至本地查阅https://www.webfalse.com/read/205515.html 文章 你不知道的JavaScript--Item3 隐式强制转换 你不知道的JavaScript--Item4 基本

javascript原型Prototype

在javaScript创建对象一文中提到过:用构造函数创建对象存在一个问题即同一构造函数的不同实例的相同方法是不一样的,所以我们用原型把构造函数中公共的属性和方法提取出来进行封装,达到让所有实例共享的目的. 接下来进一步介绍javaScript原型. 一.javaScript原型机制 1.函数与原型的关系 js中创建一个函数,就会自动创建一个prototype属性,这个属性指向函数的原型对象,并且原型对象会自动获得一个constructor(构造函数)属性,指向该函数. 举例:以前面的原型模式创

javascript原型,组合,动态原型,稳妥构造函数式

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getS

JavaScript之基础-16 JavaScript 原型与继承

一.JavaScript 原型 原型的概念 - 在JavaScript中,函数本身也是一个包含了方法和属性的对象 - 每个函数中都有一个prototype属性,该属性引用的就是原型对象 - 原型对象是保存共享属性值和共享方法的对象 为对象扩展属性 - 扩展单个对象的成员 - 扩展共享的属性值 - 内存图描述 删除属性 - 可以使用delete关键字删除对象的属性 自由属性与原型属性 - 自有属性:通过对象的引用添加的属性;其它对象可能无此属性;即使有,也是彼此独立的属性 emp1.job = '

理解JavaScript原型

Javascript原型总会给人产生一些困惑,无论是经验丰富的专家,还是作者自己也时常表现出对这个概念某些有限的理解,我认为这样的困惑在我们一开始接触原型时就已经产生了,它们常常和new.constructor相关,特别是函数(function)的原型(prototype)属性(property).事实上,原型是一种非常简单的概念.为了更好的理解它,我们应该首先记住这个原则,那就是忘记我们已经学到的关于构造原型(construtor prototypes)的认识. 什么是原型? 原型是一个对象,

深入理解JavaScript原型链

前言 最近碰到一个题,大家可以试下. Object.prototype.a = function() { console.log("aaa "+this.name); }; Function.prototype.b = function() { console.log("bbb "+this.name); }; function Person(name) { this.name = name; } var person = new Person("Chin

javascript原型

在javaScript创建对象一文中提到过:用构造函数创建对象存在一个问题即同一构造函数的不同实例的相同方法是不一样的,所以我们用原型把构造函数中公共的属性和方法提取出来进行封装,达到让所有实例共享的目的. 接下来进一步介绍javaScript原型. 一.javaScript原型机制 1.函数与原型的关系 js中创建一个函数,就会自动创建一个prototype属性,这个属性指向函数的原型对象,并且原型对象会自动获得一个constructor(构造函数)属性,指向该函数. 举例:以前面的原型模式创

深入理解javascript原型和闭包(15)——闭包

http://www.cnblogs.com/wangfupeng1988/p/3994065.html 深入理解javascript原型和闭包(15)——闭包