一、调用位置
先看看什么是调用栈和调用位置
function baz() {
// 当前调用栈是baz
// 因此,当前调用位置是全局作用域
console.log("baz")
bar()
}
function bar() {
// 当前调用栈是baz -> bar
// 因此,当前调用位置在baz中
console.log("bar")
foo()
}
function foo() {
// 当前调用栈是baz -> bar -> foo
// 因此,调用位置在bar中
}
baz() // <- baz的调用位置
二、绑定规则
1.默认绑定
最常见的函数调用类型:独立函数调用,可以看作是无法应用其他规则时的默认规则
function foo() {
console.log(this.a)
}
var a = 2
foo(); // 2
var 声明了一个全局变量a,理解为全局对象的一个属性,foo调用时,this指向全局对象
但是如果使用了严格模式
function foo() {
"use strict"
console.log(this.a)
}
var a = 2
foo(); //TypeError: this is undefined
值得注意的是,foo只有运行在非严格模式下,默认绑定才是全局对象;但是在严格模式下调用不会影响默认绑定
function foo() {
console.log(this.a)
}
var a = 2
(function(){
"use strict"
foo() // 2
})()
2.隐式绑定
另一条需要考虑的是调用位置是否有上下文对象
function foo() {
console.log(this.a)
}
var obj = {
a: 2,
foo: foo
}
obj.foo(); // 2
obj调用它,所以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
obj2调用它,所以this指向obj2
this.a === obj2.a
被隐式绑定的函数会丢失默认绑定对象,也就是说他会应用默认绑定。从而把this绑定到全局对象或者undefined上
function foo() {
console.log(this.a)
}
var obj = {
a: 2,
foo: foo
}
var bar = obj.foo; //函数别名
var a = 'global'
bar(); // 'global'
实际上bar是对obj.foo的一个引用,但是obj.foo引用的还是foo函数本身,因此应用了默认绑定
还有一种情况发生在回调函数里面
function foo() {
console.log(this.a)
}
function doFun(fn) {
fn()
}
var obj = {
a: 2
foo: foo
}
var a = 'global'
doFun(obj.foo) // 'global'
参数传递其实就是一种隐式赋值,因此我们传入函数时也会被隐式赋值
3.显式绑定
1.硬绑定
function foo() {
console.log(this.a)
}
var obj = {
a: 2
}
var bar = function() {
foo.call(obj)
}
bar() // 2
setTimeout(bar, 200); // 2
硬绑定的典型应用场景就是创建一个包裹函数,负责接收参数并返回值
function foo(something) {
console.log(this.a, something)
return this.a + something
}
var obj = {
a: 2
}
var bar = function() {
return foo.apply(obj, arguments)
}
var b = bar(3) //2 3
console.log(b) //5
另一种方法是创建一个可以重复使用的辅助函数:
function foo(something) {
console.log(this.a, something)
return this.a + something
}
// 简单的辅助函数
function 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
2.API调用上下文
第三方库的许多函数,以及JavaScript语言的宿主环境中许多新的内置函数,都提供了一个可选的参数,通常被成为上下文,其作用和bind一样,确保你的回调函数使用指定的this
function foo(e) {
console.log(e, this.id)
}
var obj = {
id: 'awesome'
}
[1,2,3].forEach(foo, obj); // 1 awesome 2 awesome 3 awesome
4.new绑定
这是第四条也是最后一条绑定规则
使用new来调用函数,或者说发生构造函数调用时,会自动执行下面的操作。
1.创建一个全新的对象
2.这个对象会被执行prototype连接
3.这个新对象会绑定到函数调用this
4.如果这个函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象
function foo() {
this.a = a
}
var bar = new foo()
console.log(bar.a) //2
三、优先级
了解了函数调用中this绑定的四条规则,我们需要找到函数的调用位置并判断应当适用哪条规则,如果某个调用位置适应多条规则,就出现了一个优先级的问题
默认绑定是优先级最低的,看看隐式绑定和显是绑定之间的优先级
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
可以看到显是绑定优先级比隐式绑定高
现在需要比较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
可见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
4.this词法
前面介绍的四条规则已经可以包含所有正常的函数。但是ES6中介绍了一种无法使用这些规则的特殊函数:箭头函数。
先看看箭头函数的词法作用域:
function foo() {
return (a) => {
console.log(this.a)
}
}
var obj1 = {
a: 2
}
var obj2 = {
a: 3
}
var bar = foo.call(obj1);
bar.call(obj2); // 2
总结
提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。