04-javascript中的this

129 阅读4分钟

wwh w:what是什么? w:why为什么? h:how怎么样?(本文总结自coderwhy老师的课)

this是什么

在常见的编程语言中,几乎都有this这个关键字(Objective-C中使用的是self),但是JavaScript中的this和常见的面向对象语言中的this不太一样:

常见面向对象的编程语言中,比如Java、C++、Swift、Dart等等一系列语言中,this通常只会出现在类的方法中。

也就是你需要有一个类,类中的方法(特别是实例方法)中,this代表的是当前调用对象。

但是JavaScript中的this更加灵活,无论是它出现的位置还是它代表的含义。

为什么要有this

为了方便编码

有this:

var obj = {
  name: "wwh",
  eating: function() {
    console.log(this.name + "在吃东西")
  },
  running: function() {
    console.log(this.name + "在跑步")
  },
  studying: function() {
    console.log(this.name + "在学习")
  }
}

没this:

var obj = {
  name: "wwh",
  eating: function() {
    console.log(obj.name + "在吃东西")
  },
  running: function() {
    console.log(obj.name + "在跑步")
  },
  studying: function() {
    console.log(obj.name + "在学习")
  }
}

this指向?(怎么做

全局指向?

  • 在浏览器中测试就是指向window

  • 在node的里是{}

因为在node里,会把一个文件当作一个模块,会去加载这个模块,会把所有代码,放到一个函数里面。然后执行这个函数(.apply({})),

图片.png

p 1.函数在调用时,JavaScript会默认给this绑定一个值;

p 2.this的绑定和定义的位置(编写的位置)没有关系;

p 3.this的绑定和调用方式以及调用的位置有关系;

p 4.this是在运行时被绑定的;

4种指向

默认绑定

独立的函数调用我们可以理解成函数没有被绑定到某个对象上进行调用;

这5个案例,this都输出指向window


function foo() {
  console.log(this)
}

foo()

function foo1() {
  console.log(this)
}
function foo2() {
  console.log(this)
  foo1()
}
function foo3() {
  console.log(this)
  foo2()
}

foo3()

var obj = {
  name: "wwh",
  foo: function() {
    console.log(this)
  }
}

var bar = obj.foo
bar() // window

function foo() {
  console.log(this)
}
var obj = {
  name: "why",
  foo: foo
}

var bar = obj.foo
bar() // window

function foo() {
  function bar() {
    console.log(this)
  }
  return bar
}

var fn = foo()
fn() // window

隐式绑定

也就是它的调用位置中,是通过某个对象发起的函数调用。

// 隐式绑定: object.fn()
// object对象会被js引擎绑定到fn函数的中this里面

function foo() {
  console.log(this)
}

// 独立函数调用
foo()

// 1.案例一:
var obj = {
  name: "why",
  foo: foo
}

obj.foo() // obj对象

// 2.案例二:
var obj = {
  name: "why",
  eating: function() {
    console.log(this.name + "在吃东西")
  },
  running: function() {
    console.log(obj.name + "在跑步")
  }
}

var fn = obj.eating
fn()

// 3.案例三:
var obj1 = {
  name: "obj1",
  foo: function() {
    console.log(this)
  }
}
var obj2 = {
  name: "obj2",
  bar: obj1.foo
}

obj2.bar()

显示绑定

function foo() {
  console.log("函数被调用了", this)
}

// 1.foo直接调用和call/apply调用的不同在于this绑定的不同
foo直接调用指向的是全局对象(window)
foo()

var obj = {
  name: "obj"
}

// call/apply是可以指定this的绑定对象
foo.call(obj)
foo.apply(obj)
foo.apply("aaaa")


// 2.call和apply有什么区别?
function sum(num1, num2, num3) {
  console.log(num1 + num2 + num3, this)
}

sum.call("call", 20, 30, 40)
sum.apply("apply", [20, 30, 40])

// 3.call和apply在执行函数时,是可以明确的绑定this, 这个绑定规则称之为显示绑定

new绑定

// 我们通过一个new关键字调用一个函数时(构造器), 这个时候this是在调用这个构造器时创建出来的对象
// this = 创建出来的对象
// 这个绑定过程就是new 绑定

function Person(name, age) {
  this.name = name
  this.age = age
}

var p1 = new Person("why", 18)
console.log(p1.name, p1.age)

var p2 = new Person("kobe", 30)
console.log(p2.name, p2.age)


var obj = {
  foo: function() {
    console.log(this)
  }
}

内置函数绑定

// 1.setTimeout
setTimeout(function() {
  console.log(this) // window
}, 2000)

// 2.监听点击
const boxDiv = document.querySelector('.box')
boxDiv.onclick = function() {
  console.log(this)
}
boxDiv.addEventListener('click', function() {
  console.log(this)
})

// 3.数组.forEach/map/filter/find
var names = ["abc", "cba", "nba"]
names.forEach(function(item) {
  console.log(item, this)
}, "abc")

优先级比较

我不知道,除了面试题会这样考,这样的代码还有什么意义?


显式 > 隐式

var obj = {
  name: "obj",
  foo: function() {
    console.log(this)
  }
}

obj.foo()

// 1.call/apply的显示绑定高于隐式绑定
obj.foo.apply('abc')
obj.foo.call('abc')

// 2.bind的优先级高于隐式绑定
var bar = obj.foo.bind("cba")
bar()


// 3.更明显的比较
function foo() {
  console.log(this)
}

var obj = {
  name: "obj",
  foo: foo.bind("aaa")
}

obj.foo()

new > 显式和隐式

var obj = {
  name: "obj",
  foo: function() {
    console.log(this)
  }
}

// new的优先级高于隐式绑定
var f = new obj.foo()


// 结论: new关键字不能和apply/call一起来使用
// new的优先级高于bind
function foo() {
  console.log(this)
}

// var bar = foo.bind("aaa")

// var obj = new bar()

总结:new绑定 > 显示绑定(apply/call/bind) > 隐式绑定(obj.foo()) > 默认绑定(独立函数调用)

特殊绑定

忽略显示绑定

function foo() {
  console.log(this)
}

foo.apply("abc")
foo.apply({})

// apply/call/bind: 当传入null/undefined时, 自动将this绑定成全局对象
foo.apply(null)
foo.apply(undefined)

var bar = foo.bind(null)
bar()

间接函数引用

var obj1 = {
  name: "obj1",
  foo: function() {
    console.log(this)
  }
}

var obj2 = {
  name: "obj2"
};

// obj2.bar = obj1.foo
// obj2.bar()

(obj2.bar = obj1.foo)()