再探原型模式

再探原型模式

一切都是对象

在JavaScript这门语言中,获取对象的唯一途径就是克隆,而JavaScript中的根对象是Object.prototype,我们遇到的每一个对象实际上都是从这个Object.prototype克隆而来的。但我们并不需要关系克隆的细节,因为这是引擎内部负责实现的,我们只需要调用var obj1 = new Object()或者var obj2 = {},内部引擎就会从Object.prototype上克隆一个对象出来。所以可以这么说:在JavaScript中,一切都是对象。

原型模式

我们先来看一下下面这2行代码

function F() {}
var f = new F()

这段简单的代码包含了好几个概念:

  1. 构造函数
  2. prototype属性
  3. constructor属性
  4. __proto__属性
  5. new操作符

所谓的构造函数其实就是普通的函数,叫作构造函数仅仅是告诉我们:这个函数将来是被用作创建对象的。

在JavaScript中,每一个函数都有天然的拥有5个属性:length, name, arguments, caller, prototype。

image

PS: 几个获取对象属性名的API

Object.keys(obj) // 获取obj的属性名(无法获取Symbol属性)
Object.getOwnPropertyNames(obj)  // 获取obj的属性名(无法获取Symbol属性)
Object.getOwnPropertySymbols(obj)  // 获取obj的Symbol属性名
Reflect.ownKeys(obj)  // 获取obj所有类型的键名,包括常规键名和 Symbol 键名。

函数的prototype属性指向一个对象,这个对象在创建函数的时候自动生成,并且拥有一个constructor属性,constructor属性指向这个函数。

在上面的那2行代码中,f是构造函数F()生成的对象,通过设置构造函数的prototype实现原型继承的时候,除了根对象Object.prototyp,任何对象都有一个内部属性[[Prototype]],它指向这个构造函数的原型

《javaScript高级程序设计(第三版)P148》

当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性) ,指向构造函数的原型对象。ECMA-262 第 5 版中管这个指针叫 [[Prototype]] 。虽然在脚本中没有标准的方式访问 [[Prototype]] ,但 Firefox、Safari 和 Chrome 在每个对象上都支持一个属性__proto__ ;而在其他实现中,这个属性对脚本则是完全不可见的。

image

但是现在__proto__属性已在ES6中标准化,现在更推荐使用Object.getPrototypeOf/Reflect.getPrototypeOf 和Object.setPrototypeOf/Reflect.setPrototypeOf来读写[[Prototype]]。

__proto__的读取器(getter)暴露了一个对象的内部 [[Prototype]] :

  1. 对于使用对象字面量创建的对象,这个值是 Object.prototype。
  2. 对于使用数组字面量创建的对象,这个值是 Array.prototype。
  3. 对于functions,这个值是Function.prototype。
  4. 对于使用 new fun 创建的对象,其中fun是由js提供的内建构造器函数之一(Array, Boolean, Date, Number, Object, String 等等),这个值总是fun.prototype。
  5. ==对于用js定义的其他js构造器函数创建的对象,这个值就是该构造器函数的prototype属性。==(这是最常见的形式)

__proto__ 的设置器(setter)允许对象的 [[Prototype]]被变更。前提是这个对象必须通过 Object.isExtensible(): 进行扩展,如果不这样,一个 TypeError 错误将被抛出。要变更的值必须是一个object或null,提供其它值将不起任何作用。

默认情况下,对象是可扩展的:即可以为他们添加新的属性。以及它们的__proto__ 属性可以被更改。Object.preventExtensions,Object.seal 或 Object.freeze 方法都可以标记一个对象为不可扩展(non-extensible)。

// 新对象默认是可扩展的.
var empty = {};
Object.isExtensible(empty); // === true

// ...可以变的不可扩展.
Object.preventExtensions(empty);
Object.isExtensible(empty); // === false

// 密封对象是不可扩展的.
var sealed = Object.seal({});
Object.isExtensible(sealed); // === false

// 冻结对象也是不可扩展.
var frozen = Object.freeze({});
Object.isExtensible(frozen); // === false

那么new操作符做了什么事情呢?通过new调用构造函数实际上经历了4步:

  1. 创建一个新对象
  2. 将this指向这个新对象
  3. ==执行构造函数(为新对象添加属性)==
    (PS: 这就是为什么使用new来实现继承会导致额外的构造函数调用,戳这里见详情:额外的构造函数调用
  4. 返回这个对象

理解new操作符

为了理解new操作符,我们自己动手模拟一个new(或者说,假如没有new,我们该如何实现创建对象?)

function Point(x, y) {
    this.x = x
    this.y = y
}

Point.prototype.getLength = function () {
    let {x, y} = this
    return Math.sqrt(x * x + y * y)
}

function defineClass(initializer) {
    return function f(...args) {
        f.prototype = initializer.prototype    // 确保instanceof正确
        let obj = Object.create(initializer.prototype)  //创建一个新队对象
        initializer.apply(obj, args)    // 将this指向这个对象,并执行构造函数
        return obj    // 返回这个对象
    }
}

var p1 = defineClass(Point)(3, 4)
var p2 = new Point(5, 12)
console.log([p1.x, p1.y, p1.getLength(), p1 instanceof Point])
console.log([p2.x, p2.y, p2.getLength(), p2 instanceof Point])
// [3, 4, 5, true]
// [5, 12, 13, true]

其他

Object.create

这里用到了Object.create。关于Object.create的相关内容移步这里:

关于Object.create的那点事

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容