前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >this怎么那么难呢?(接上篇-1)

this怎么那么难呢?(接上篇-1)

作者头像
用户3258338
发布2019-12-31 11:25:00
2660
发布2019-12-31 11:25:00
举报

如果没有能力做选择,那就只能被选择~


2019年12月22日,星期六。距离新年还有9天了,宝宝们可以提前想想2020年的目标了。也可以把目标留言在文章下方,目标说给别人听能起到督促自己的作用,不信你就试试!

接上篇this的绑定方式,第一种是默认绑定,也就是独立函数的调用,这种情况就看作是无法应用其他规则的默认绑定。

第二种是隐式绑定,我们接着看:

隐式绑定


调用位置是否有上下文,或者说被某个对象拥有或者包含。思考下面代码:

function foo(){
  console.log(this.a);
}
var obj = {
  a :2,
  foo: foo
}
obj.foo(); // 2

以上,首先需要注意的是foo()的声明方式,然后是如何被当做引用属性添加到obj中的。但是无论是直接在obj中定义还是先定义再添加为引用属性,这个函数严格来说都不属于obj对象。

然而,调用位置会使用obj上线文来引用函数,因此可以说函数调用时obj对象“拥有”或“包含”它。

当函数引用有上下文对象时,隐式绑定规则会把函数调用中的this绑定到这个上下文对象。因调用foo()时this被绑定到obj,因此this.a和obj.a是一样的。

对象引用链中只有最顶层或者最后一层会影响调用位置。

function foo(){
  console.log(this.a)
}
var obj2={
  a:42,
  foo: foo
}
var obj1={
  a: 2,
  obj2: obj2
}
obj1.obj2.foo()  // 42

在我看来foo被调用的位置 实际上还是obj2中,所以this指向了obj2。

隐式丢失

function foo(){
  console.log(this.a)
}
var obj = {
  a :2,
  foo: foo
}
var bar = obj.foo;  // 给将函数foo赋值给bar
var a = "000000"
bar(); // "000000" 这里才是foo真正调用的地方。是默认绑定
function foo(){
  console.log(this.a);
}
function doFoo(fn){
  fn()   // 被调用的位置
}
var obj ={
  a :2,
  foo: foo
}
var a = "123"
doFoo(obj.foo);   // "123"

把函数传入语言内置的函数而不是传入自己声明的函数,结果是一样的:

function foo(){
  console.log(this.a)
}
var obj = {
  a:2,
  foo: foo
}
var a = "hello"
setTimeout(obj.foo, 100);  // "hello"

js环境内置的setTimeout()函数实际和下面的伪代码类似:

function setTimeout(fn,delay){
  // 等待delay毫秒  
  fn(); // 真正调用位置
}

显式绑定


使用call和apply;他们的第一个参数是一个对象,他们会把这个对象绑定到this,接着调用函数时指定这个this。

function foo(){
  console.log(this.a)
}
var obj = {
  a: 2
}
foo.call(obj); //2

如果传入的第一个参数是原始值(string、boolean、number)来当做this的绑定对象,这个原始值会被转换成它的对象形式(也就是new String(...)、new Boolean()、new Number())。这通常被称作“装箱”。

function foo(){
  console.log(this.a);
}
car obj = {
  a : 2
}
var bar = function (){
  foo.call(obj)
}
bar(); // 2 
setTimeout(bar,100) // 2
bar.call(window); // 2 硬绑定的bar不可能再修改它的this

以上,创建了函数bar,并在他的内部手动调用了doo.call(obj),因此强制把foo的shis绑定到了obj。无论之后如何调用函数bar,它总会手动在obj上调用foo。这种绑定是一种显示的强制绑定,即硬绑定。

硬绑定用法1:创建一个包裹函数,传入所有的参数并返回接收到的所有值:

function foo(something){
  console.log(this.a,something);
  return this.a + something;
}
var obj = {
  a :2
}
var bar = functiong(){
  return foo.apply(obj, arguments);
}
var b = bar(3)   // 2 3
console.log(b) // 5

硬绑定用法2:创建一个i可以重复使用的辅助函数:

function foo(something){
  console.log(this.a, something);
  return this.a + something
}
functiong bind(fn, obj){
  return function (){
    return fn.apply(obj , arguments);
  }
}
var obj = {
  a : 2
}
var bar = bind( foo, obj)
var b = bar(3); // 2  3
console.log(b) // 5

硬绑定是非常常用的模式,所以ES5中提供了内置的方法Function.prototype.bind,用法如下:

function foo(something){
  console.log(this.a, something);
  return this.a + something
}
var obj = {
  a :2
}
var bar = foo.bind(obj);
var b = bar(3); // 2 3
console.log(b) // 5

bind(...)会返回一个硬编码的新函数,他会把参数设置为this的上下并调用原始函数。

new绑定


这是第四条也是最后一条this的绑定规则。

在JS中,构造函数只是一些使用new操作符时被调用的函数。他们并不会属于某个类,也不会实例化一个类。实际上,他们甚至都不会说是一种特殊的函数类型,他们只是被new操作符调用的普通函数而已。

实际上并不存在所谓的“构造函数”,只有对于函数的“构造调用”。

使用new调用函数时,具体都做了什么:

  1. 创建一个全新的对象
  2. 这个新对象会被执行【原型】连接
  3. 这个新对象会绑定到函数调用的this
  4. 如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象

优先级


默认绑定的优先级是四条规则中最低的。

我们来看看隐式绑定和显示绑定哪个优先级更高:

function foo() { 
  console.log( this.a );
}
var obj1 = { 
  a: 2,
  foo: foo 
};
var obj2 = { 
  a: 3,
  foo: foo 
 };
obj1.foo();  //  2
obj2.foo(); // 3
obj1.foo.call( obj2 );  // 3
obj2.foo.call( obj1 );  // 2

so , 显示绑定的优先级高于隐式绑定

看看new和隐式绑定的优先级:

function foo(something) { 
  this.a = something;
}
var obj1 = { 
  foo: foo
};
var obj2 = {};
obj1.foo( 2 );
console.log( obj1.a ); // 2
obj1.foo.call( obj2, 3 );
console.log( obj2.a ); // 3
var bar = new obj1.foo( 4 ); 
console.log( obj1.a ); // 2 
console.log( bar.a ); // 4

so, new绑定的优先级高于隐式绑定。但是new 和显示绑定的优先级谁更高呢?

new和call/apply无法一起使用,因此无法进行直接测试。我们用bind进行测试:

function foo(something) { 
  this.a = something;
}
var obj1 = {};
var bar = foo.bind( obj1 ); 
bar( 2 );
console.log( obj1.a ); // 2
var baz = new bar(3); 
console.log( obj1.a ); // 2 
console.log( baz.a ); // 3

以上,new 修改了硬绑定调用bar中的this.我们得到了一个新对象,并且baz.a的值是3.

也就是说优先级为:new > call/apply > 隐式绑定> 默认绑定

愿我们有能力不向生活缴械投降---Lin

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-12-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 女程序员的日常 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档