nicoleJT914 / blog

一只游行的火烈鸟/用issues记博客
0 stars 0 forks source link

this #7

Open nicoleJT914 opened 7 years ago

nicoleJT914 commented 7 years ago

this的作用

function showName() {
  console.log(this.name)
}
var user = {
  name: 'xiaoMing'
}
showName.call(user)

function showName(user) {
  console.log(user.name)
}
var user = {
  name: 'xiaoMing'
}
showName(user)

随着使用模式越来越复杂,显式传递上下文对象会让代码变得越来越混乱 而this提供了一种更隐式方式来传递一个对象引用,使得代码的复用性提高

this的绑定规则

默认绑定

无法应用其他规则时的默认规则(也是坑最多的地方)

function foo() {
  console.log(this.a)
}
var a = 2
foo() // 2

隐式绑定

当函数引用有上下文对象时,隐式绑定规则会把函数调用中的 this 绑定到这个上下文对象。

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

如果传入了一个原始值(字符串类型、布尔类型或者数字类型)来当作 this 的绑定对象,这个原始值会被转换成它的对象形式(也就是new String(..)、new Boolean(..)或者 new Number(..))。这通常被称为“装箱”。

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)
console.log(b)

bind() 会返回一个硬编码的新函数,它会把参数设置为 this 的上下文并调用原始函数,要注意bind()会返回新函数,但并不会像apply()call()一样会直接调用函数,所以bind()更常用为回调函数指定this

new绑定

JS中并不存在所谓的“构造函数”,只有对于函数的“构造调用”

function foo(a) {
  this.a = a
}
var bar = new foo(2)
console.log(bar.a)

4条绑定规则的优先级

  1. var bar = new foo()
  2. var bar = foo.call(obj)
  3. var bar = obj.foo()
  4. var bar = foo()

} obj1.foo(2) console.log(obj1.a) // 2

obj1.foo.call(obj2, 3) console.log(obj2.a) // 3 console.log(obj1.a) // 2

var bar = new obj1.foo(4) console.log(obj1.a) // 2 console.log(bar.a) // 4

new绑定的优先级更高

- 比较 硬绑定 和 new绑定

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 调用,如果是的话就会使用新创的 this 替换硬绑定的 this

- 为什么要在 new 中使用硬绑定函数?
主要目的是预先设置函数的一些参数,这样在使用 new 进行初始化时就可以只传入其余的参数
bind(..) 的功能之一就是可以把除了第一个参数(第一个参数用于绑定 this)之外的其他参数都传给下层的函数(这种技术称为“部分应用”,是“柯里化”的一种)

function foo(p1, p2) { this.val = p1 + p2 } var bar = foo.bind(null, "p1") var baz = new bar("p2") baz.val // p1p2

### 绑定例外
- 把 null 或者 undefined 作为 this 的绑定对象传入 call、apply 或者 bind,这些值在调用时会被忽略,实际应用的是默认绑定规则:

function foo(a, b) { console.log("a:" + a + ",b:" + b) } // 使用 null 来忽略 this 绑定 foo.apply(null, [1, 2]) var bar = foo.bind(null, 5) bar(6)

在ES6中可以用`...`操作符来代替`apply()`来展开数组

function foo(a, b) { console.log("a:" + a + ",b:" + b) } foo(...[1,2])

用`null``来忽略this绑定可能产生副作用,更安全的做法是这是空对象

function foo(a, b) { console.log("a:" + a + ",b:" + b) } var ø = Object.create(null) foo.apply(ø, [2, 3]) var bar = foo.bind(ø, 2) bar(3)


- 箭头函数
箭头函数不使用this的四种规则
nicoleJT914 commented 7 years ago

踩坑Array.prototype.forEach

array.forEach(callback(currentValue, index, array){
    //do something
}, this)

forEach还有参数this,不指定的话默认是undefined 因此要么提前绑定this,要么在forEach中指定this

nicoleJT914 commented 7 years ago

怎样在定时器setTimeout中锁定this? 用bind()呀!

setTimeout(fn.bind(this), xx)

Ps: 顺便提一句,关于怎样在setTimeout中使用传递参数的函数

// 回调函数
function callback(element) {}
// 对回调函数形式进行修改
setTimeout(fn(element), xx)
function fn(element) {
  return callback() {
  }
}
nicoleJT914 commented 7 years ago

SO上有关于this的运用,已经比较清楚了 How to access the correct this context inside a callback?

nicoleJT914 commented 7 years ago

最上面关于this的解释太麻烦了,重新梳理一下

构造函数

function Foo() {
  this.name = 'jack',
  this.age = 35
  console.log(this)
}
Foo.prototype.say = function() {
  console.log(this.name)
}
var foo = new Foo()
foo.say()
console.log(foo.name)
console.log(foo.age)

若函数为构造函数,则this指向实例化的对象

函数作为对象的一个属性

当函数作为对象的一个属性并且被直接调用时,函数中的this指向该对象

var o = {
  x: 10,
  fn: function() {
    console.log(this)  // o
    console.log(this.x)
  }
}
o.fn()

注意:只有直接调用才指向该对象

var o = {
  x: 10,
  fn: function() {
    console.log(this)  // Window
    console.log(this.x)
  }
}
var s = o.fn
s()

函数用call或者apply调用

全局 & 调用普通函数

nicoleJT914 commented 7 years ago
var length = 10;
function fn() {
  console.log(this.length);
}
var obj = {
  length: 5,
  method: function(fn) {
    fn();
    // 实质上是arguments对象属性的读取,所以this是arguments对象
    arguments[0]();
  }
};
obj.method(fn, 1);
// result should be 10 2