YDKJS-this与对象原型

266 阅读5分钟

主要内容:回答问题

这一小册回到了两个问题:

  1. this关键字的指向如何确定?
  2. 对象原型系统实现的工作方式:行为委托 ,是什么?

下面的所有内容在回答剖析这些问题。

this关键字的指向如何确定?

为什么使用this?为什么要有this指向执行环境的机制?

在使用this时,this指向的是执行环境这个对象,在函数内部获取对象数据,也可以通过函数传参实现。两个demo实现的是一样功能:

  1. demo1:使用this
  2. demo2:函数传参

首先使用this的机制更优雅更清晰,其次在之后探索了this指向机制之后,会感受到使用this的机制更智能(否则得手动确认context是什么)

this指向如何确定?

this指向的确定分规则、规则优先级、特例三方面揭示。

规则

独立函数调用(直白的、毫无修饰的函数引用调用),默认绑定this指向全局对象

  • 特例:如果函数内容在严格模式下,thisundefined

调用点有一个环境对象/拥有者/容器,隐含绑定this指向拥有者/容器

  • 补充:这个环境对象/拥有者/容器不真正"拥有"/"包含"这个函数,只是函数引用;遇到形如obj1.obj2.foo(),只有最后一层确定this指向
  • 特例:隐含丢失,将形如obj1.foo或赋值或作为参数传递(比如回调函数)导致隐含丢失,回到默认绑定规则

apply/call/bind,或者是某些API函数提供一个指向"环境"的可选参数,明确绑定this指向明确对象。写了有意思的demo:

  1. 使用apply/call模拟bind
  2. 自定义实现apply/call/bind
    • callapply自定义实现过程,第一个注意点是,context这个值不一定是对象,所以才有了在最开始对context的转换;第二个注意点是,利用隐含绑定去实现明确绑定,需要在函数调用后delete添加的属性(挂载了函数的)
    • bind自定义实现过程,借用了calldiy/applydiy,但有个注意点是柯里化,即参数可以在bind阶段传部分,也可以在函数执行阶段传递,有个参数的拼接

new+函数的构造器调用,new 绑定。构造器调用自动发生四个步骤:

  1. 创建一个全新的对象
  2. 新创建的对象接入原型链
  3. 这个对象作为函数调用的this指向
  4. 除非函数返回一个其他对象,否则这个对象作为构造器调用的返回结果
规则优先级

new绑定>明确绑定>隐含绑定>默认绑定

  1. 证明明确绑定>隐含绑定
  2. 证明new绑定>隐含绑定
  3. 证明new绑定>明确绑定
特例
  1. 传递null/undefined作为callapplybindthis绑定参数,默认绑定works
    • 因为有时候不在意this指向是什么,所以放了null/undefined,但是这样可能会导致全局对象的污染,所以建议使用Object.create(null)一个完全为空(没有指向Object.prototype的委托)的对象,这样更安全
  2. 隐含丢失,默认绑定works
  3. 箭头函数中的this:从封闭它的(函数或全局)作用域采用this绑定(回到词法作用域)

对象原型系统实现的工作方式:行为委托 ,是什么?

简述一下对象(js类型、对象子类型、属性等等)

(在确定this指向之后,自然而然目标集中到了对象,了解对象是了解行为委托的基础)

这一块以前写了一篇文章总结:YDKJS-对象那些事儿

以下几个要多加关注:

  • 复制对象

  • for..of迭代值需要迭代器对象(数组、对象对应的情况)

  • 对象属性遍历的方法和对应出来的顺序:只有for..in去查看原型链了

简述下[[Prototype]]机制

[[Prototype]]是对象的一个内部属性,它指向一个其他对象。当一个属性/方法引用在一个对象上发生,而这样的属性/方法又不存在时,[[Prototype]]链就会被使用,沿着原型链去寻找。链的最顶端是Object.prototype

主要内容之外:委托与类/继承的比较

在JavaScript的软件体系结构中,类/继承、委托是可以选择的两种设计模式。结合实例介绍一下类理论和委托理论:

  1. 类理论:有一个泛化的父类,定义共享的行为,子类继承父类,子类添加特化的行为。类实例化,与实例交互。

  2. 委托理论:有一个工具对象(委托),定义工具方法,将特定任务对象(委托者)链接到Task对象

  3. 思维模型的比较:学术派的代码用OO和OLOO两种方法实现:OO风格OLOO风格,对应OO思维模型图OLOO思维模型图

  4. 更具体的代码场景:建造UI部件

行为委托更加强大也更加简洁,总结OLOO风格的代码有以下三个注意点:

  1. 数据成员/状态,保持再委托者上,而不是委托
  2. 避免在[[Prototype]]链不同层级给出相同命名,反而推荐针对对象具体行为给出更具描述性的方法名
  3. 委托更适合作为内部实现细节:委托作为内部实现细节