美文网首页
上卷 第二部分 第五章 原型

上卷 第二部分 第五章 原型

作者: nymlc | 来源:发表于2019-07-09 19:21 被阅读0次

__proto__

除了ES6的Proxy,均是之前说过的[[Get]]、[[Put]]

  1. Object.prototype:所有普通(内置,不是特定主机的扩展)的__proto__的尽头
  2. 属性的设置和屏蔽(属性设置完整过程)
obj.foo = 'bar'

// 1. 如果obj对象上包含名为foo的属性,那么只会修改已有的属性值
// 2. 如果foo不在obj上,那么\_\_proto\_\_会被遍历
// 3. 如果\_\_proto\_\_上面找不到,那么foo会被添加到obj上
// 4. 如果\_\_proto\_\_上面找到了,那么就复杂了。

// ps:总会取原型链底层的属性

// 如果foo不直接存在obj上,而是在其原型链上,该赋值会出现以下三种情况
// 1. 如果foo被标记为writable: true(writable默认true),那么会直接在obj上添加foo这个新属性,它是屏蔽属性
// 2. 如果被标记为writable: false,那么无法修改已有的属性,或者创建屏蔽属性。在严格模式下还会抛出错误(TypeError)
// 3. 如果存在setter,那么会调用这个setter
// 值得注意的是,2、3可以使用Object.defineProperty来越过(可以屏蔽属性)。也就是=操作符才会符合上面俩情况
"use strict";
var o = {}
Object.defineProperty(o, 'foo', {
    value: 'bar',
    writable: false
})
var obj = Object.create(o)
obj.foo = 123

第二种情况主要是模拟类属性继承。比如o是父类,obj是子类。o的属性foo是只读,它被obj继承,自然也是只读,所以obj无法创建foo。这个限制只在=操作符上

"类"

原型链使得一个对象关联到另外一个对象,但是为什么要这样子做呢?

  1. 类函数
    PS:这里讲一个误区。一说起原型就会想到函数对象的prototype属性,其实这只是函数对象的一个不可枚举的属性,它的指向的对象才是原型
function Foo() {

}
var foo = new Foo()

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

new Foo()时,会创建foo对象,这个对象的__proto__会被指向Foo.prototype
回忆一下上一章所说的。若是面向类的语言之中,类的实例化其实是把类的行为复制到物理对象里(可操作的),每一个新实例都是这样子。
不过在JS里没有类的复制,只能创建多个对象,这些对象都通过__proto__关联到Foo.prototype
也就是我们并没有实例化一个类(因为我们没有从一个类复制任何行为到一个对象),只是关联了俩对象。值得注意的是,这里说的类很明显指的是Foo.prototype,呼应上一章

其实上面所说的就是原型继承,不过这个名字很明显有问题(JS里就没有继承这回事)。其实正确的叫法应该是委托。毕竟对象本身上没有的行为通过关联,可以在另外一个对象上找到。

对象之间的关系不是复制而是委托

差异继承:描述对象行为时,只描述其与不同于普遍的特征(就像描述白种人就说他们金发碧眼,而不会说他们俩只眼睛)。其实还是委托比较符合

Object.prototype.clone = function() {
    var o = Object.create(this)
    return o
}

var root = {}

var person = root.clone()
person.name = 'person'
person.toString = function() {
    return this.name
}
var oli = person.clone()
oli.name = 'oli'

onload.toString() // oli
  1. 构造函数
    主要是几点让我们觉得JS里有类。一个是new,一个是Foo()有点像构造函数调用,一个是Foo.prototype.constructor这个对象(.constructor指向的是对象关联的函数,值得注意的是构造出来的对象也能引用这个属性,不过这个对象本身没有这个属性)

在JS中没有构造函数,当且仅当使用new是,函数调用会变成构造函数调用

  1. 技术
    上面说道constructor是在.prototype上的。这个其实很坑。可以被修改。o.constructor是一个非常不靠谱的引用

(原型)继承

原型继承
function Foo(name) {
    this.name = name
}
Foo.prototype.myName = function () {
    return this.name
}

function Bar(name, label) {
    Foo.call(this, name)
    this.label = label
}
// 这句是核心,指代的是上图的那条线
// ES5语法,会抛弃默认的prototype,比如constructor被破坏了
Bar.prototype = Object.create(Foo.prototype)
// ES6方法,可以不用重写Bar.prototype
Object.setPrototypeOf(Bar.prototype, Foo.prototype)
// 俩点误区
// Bar.prototype = Foo.prototype,这个肯定不行,因为扩展子类行为会影响到父类
// Bar.prototype = new Foo(),这个倒是没有上面这个问题,不过有个hen严重的问题,调用了Foo,那么Foo函数会被莫名调用一次

Bar.prototype.myLabel = function () {
    return this.label
}

var bar = new Bar('bar', 'child')

console.log(bar.myName(), bar.myLabel()) // bar child
  1. 检查”类“关系
    寻找一个对象的委托对象就像在Java里检查一个实例的继承祖先(反射),有俩种方法
function Bar() {

}
Bar.prototype.myLabel = function () {
    
}

var bar = new Bar()
  • bar instanceof Bar:这个操作符的意思就是在左值的整条原型链上是否有右值.prototype指向的对象。很明显,这只能处理对象(左值)和函数对象(右值)的关系。
var a = ''
a.__proto__ === String.prototype // true ①
a instanceof String // false ②
// 这点很奇怪,按理a的原型链上的确出现了String.prototype,不过instanceof却是false
// 这里其实有点隐蔽,因为a不是对象,instanceof时,它被转成包装对象,那么①处当然是true,不过a始终只是个字符串,所以②处自然是false
// 因为instanceof不能直接判断俩个对象之间的关系,所以可以使用下面这个方法
// 不过得看理解角度,在类这个角度而言,很是荒谬,毕竟o1很明显没有继承F,不过判断对象关联还是有可取的
function isRelatedTo(o1, o2) {
    function F() {}
    F.prototype = o2;
    return o1 instanceof F;
}
var a = {};
var b = Object.create(a)
isRelatedTo(b, a) // true

如果右值是用.bind()生成的硬绑定函数,那么是没有.prototype属性的,这时候不会报错,会使用目标函数的.prototype属性(其实实际上相当于直接调用目标函数,instanceof也就相当于直接使用目标函数)

  • Bar.prototype.isPrototypeOf(bar):这个好处很明显,无需考虑是不是函数对象。a.isPrototypeOf(b) // a是否出现在b的原型链之上,就是上文的isRelatedTo方法
    PS:__proto__这个属性不是兼容所有的浏览器(在ES6之前不是标准)。它存在Object.prototype上。大致如下
Object.defineProperty(Object.prototype, '__protp__', {
    get() {
        return Object.getPrototypeOf(this)
    },
    set(o) {
        Object.setPrototypeOf(this, o)
    }
})

对象关联

嗯,之前说的__proto__准确的描述应该是[[Prototype]]机制,毕竟这个属性在ES6之前不是标准,不是所有的浏览器都提供

  1. 创建关联
    Object.create
    值得注意的是,Object.create(null)会创建一个空[[Prototype]]链接对象。因为没有原型链,所以instanceof无法判断。这个对象也被叫做字典,不受原型链干扰,适合存储数据
  2. 关联关系是备用
    关联关系可以是处理对象缺失属性或者非法的一种备用选项。不过这可能并不是个好事。比如你本来没有设计这个方法,结果居然可以运行,那么不就很神奇了。就像如下:
var anotherObject = {
    output: function () {
        console.log('output')
    }
};
var myObject = Object.create(anotherObject)
myObject.output() // output

其实可以修改下(委托设计模式),就是在实际存在的方法里面委托到别的方法处理,内部委托比直接委托可以让API接口更清晰

var anotherObject = {
    output: function () {
        console.log('output')
    }
};
var myObject = Object.create(anotherObject)
myObject.doOutput = function() {
    this.output()
}
myObject.doOutput() // output

常用polyfill

  1. new
function New() {
    var args = Array.prototype.slice.call(arguments)
    var F = args.shift()
    var o = {}
    o.__proto__ = F.prototype

    var res = F.apply(o, args)
    if ((typeof res === 'object' || typeof res === 'function') && res !== null) {
        return res
    }
    return o
}
function Car(year) {
    this.year = year;
    // return function () { }
}
var car1 = New(Car, 2019)
car1
// 使用Object.create
function New() {
    var args = Array.prototype.slice.call(arguments)
    var F = args.shift()
    var o = Object.create(F.prototype)
    var res = F.apply(o, args)
    if ((typeof res === 'object' || typeof res === 'function') && res !== null) {
        return res
    }
    return o
}
function Car(year) {
    this.year = year;
    // return function () { }
}
var car1 = New(Car, 2019)
car1
  1. Object.create
if (!Object.create) {
    Object.create = function (o) {
        function F() { }
        F.prototype = o
        return new F()
    }
}

相关文章

  • 上卷 第二部分 第五章 原型

    __proto__ 除了ES6的Proxy,均是之前说过的[[Get]]、[[Put]] Object.proto...

  • 《被动》目录 完结

    《被动》上卷 知有飘零 目录 《被动》 上卷 楔子 《被动》上卷 第一章 囚鸟 《被动》上卷 第二章 相聚 《被动...

  • 作用域 | 闭包 | 模块 | 词法作用域 |

    以下内容为《你不知道的JavaScript上卷》第一部分第五章-作用域闭包及附录的阅读笔记 一.作用域闭包 1.是...

  • 深入研究原型

    以下笔记参考自《你不知道的JavaScript上卷》 一.原型 1.[[Prototype]]是什么? 它是Jav...

  • 部分原型图

    一、首次进入软件的选择题 二、消息页 三、通知页 四、我

  • (原型)继承 | 对象关联

    该笔记参考自《你不知道的JavaScript上卷》 一.(原型)继承 1.检查"类"关系 假设现在有个对象a,如果...

  • this到底是谁?

    《你不知道的JavaScript上卷》第二部分第1章-第2章 1.是指向自身吗? 我们都知道在JavaScript...

  • 第二部分 原型

    5.1 [[Prototype]] 当你试图引用对象的属性时会触发[[Get]]操作,比如myObject.a。对...

  • 打卡读书笔记

    打卡【SPIN销售 高价成交】 【时间】DAY5-0330 【部分】第二部分 价值创造和战略型销售 【章节】第五章...

  • 设计模式_2

    第二部分 对 象 创 建 第3章 原 型 原型摸式是一种非常简单的设计模式。使用原型实例指定创建对象的种类...

网友评论

      本文标题:上卷 第二部分 第五章 原型

      本文链接:https://www.haomeiwen.com/subject/tudstctx.html