高级JS-this指向(重点)

62 阅读4分钟

this指向

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

this是在运行时被绑定的

  • 默认绑定
<script>
    // 严格模式,独立函数调用:undefined
    "use strict"

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

    // 1. 默认绑定(独立函数调用)
    foo() // window

    // 2. 函数定义在对象中,但是独立调用
    var info = {
        name: 'qqq',
        baz: function () {
            console.log('我是baz函数', this);
        }
    }
    var bar = info.baz
    bar() // window(独立函数调用)

    info.aaa = foo
    info.aaa() // obj对象
    
   // 3. 高阶函数
   function test(fn) {
    fn()
   }
  test(info.baz)  // window
</script>
  • 隐式绑定
// 隐式绑定
function foo() {
    console.log(this);
}
var obj = {
    bar : foo
}
obj.bar() // obj对象

var obj2 = {
    bar2 : obj
}
obj2.bar2.bar() // obj对象
  • new 绑定
/*  new操作符做的事
    1. 创建一个空对象
    2. 将this指向这个空对象
    3. 执行函数体的代码
    4. 如果函数没有返回其他对象,表达式会返回这个新对象
*/
function foo() {
    console.log(this);
}
new foo() // foo对象
  • 显示绑定
 function foo() {
     console.log(this);
 }
var obj = {
     name:'www'
}

// 执行函数,强制this就是obj对象
 foo.call(obj) // obj对象
 foo.apply(obj) // obj对象
 foo.call(123) // Number{123} 包装类
 foo.call(undefined) // window
 
// 调用foo时,不希望obj对象上有函数
var bar = foo.bind(obj) // bind() 方法创建一个新的绑定函数
bar() // obj对象

call 和 apply 的区别

function foo(name,age,create) {
    console.log(this);
    console.log('参数列表:'+ name,age,create); // wwqq 20 新增
}
var obj = {name: 'wqq'}

/*
  第一个参数:绑定的this
  第二个参数:以数组的形式传入实参
*/
foo.apply('apply',['www',19])

/*
  第一个参数:绑定的this
  第二个参数:以参数列表的形式传入实参
*/
foo.call('call','qqq',20)

/* 了解 */
var bar = foo.bind(obj,'wwqq',20)
bar('新增') // obj对象

内置函数的调用绑定

<button>按钮</button>

setTimeout(function () {
   console.log('定时器函数:',this);  // window
},1000)

var btnEl = document.querySelector('button')

btnEl.onclick = function () {
   console.log('btn的点击', this); // <button>按钮</button>
}

btnEl.addEventListener('click',function () {
   console.log('btn的点击', this); // <button>按钮</button>
})

var names = ['ww','qq','wqq']
names.forEach(function (item) {
   console.log('forEach', this); // window
})
names.forEach(function (item) {
   console.log('forEach', this); // aaa
},'aaa')

this绑定优先级

  1. 显示绑定 > 隐式绑定
  2. new绑定 > 隐式绑定
  3. new绑定 > bind
  4. bind > apply / call
function foo() {
    console.log(this);
}

// 1. 显示绑定 > 隐式绑定
var bar = foo.bind('bbb')
var obj = {
    foo: foo,
    objBar: bar,
    baz: function () {
        console.log('我是baz函数:', this);
    }
}
obj.foo.apply('abc') // abc
obj.objBar()  // bbb

// 2. new绑定 > 隐式绑定
new obj.baz() // baz{}

// 3. new绑定 > bind
/*  3.1 new绑定和call、apply是不允许同时使用的,所以不存在谁的优先级更高
    3.2 new绑定可以和bind一起使用,new绑定优先级更高
 */
var bindFn = foo.bind('ccc')
new bindFn()  // foo{}空对象

// 4. bind > apply / call
var bindFoo = foo.bind('ddd') 
bindFoo.apply('jjj') // ddd
bindFoo.call('kkk') // ddd
  • 忽略显示绑定
function foo() {
    console.log(this);
}
// 如果在显示绑定,传入一个null或者undefined,则使用默认绑定
foo.apply('abc') // abc
foo.apply(null) // window, 严格模式:null
foo.apply(undefined) // window, 严格模式:undefined
  • 间接函数引用
var obj1 = {
    name:'obj1',
    foo: function () {
        console.log('foo:',this);
    }
}
var obj2 = {
    name:'obj2'
};
(obj2.foo = obj1.foo)() // window

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

箭头函数(arrow function)

  1. 箭头函数不会绑定this、arguments属性
  2. 箭头函数不能作为构造函数来使用(不能和new一起来使用,会抛出错误)
var baz = (name) => {
    console.log('箭头函数的函数体');
}

var names = ['aaa', 'bbb', 'ccc']
names.forEach((item, index, arr) => {
    console.log(item, index, arr);
})

// 1. 如果只有一个参数, ()可以省略
names.forEach(item => {
    console.log(item);
})

// 2. 如果函数执行体中只有一行代码, 那么可以省略大括号
names.forEach(item => console.log(item))

// 3. 只有一行代码时,这行代码的返回值会作为整个函数的返回值
var nums = [12, 32, 43]
var newNum = nums.filter(item => item % 2 === 0)

// 4. 如果默认返回是一个对象, 那么需要给这个对象加上()
var arrFn = () => ({name: 'www'})
arrFn() // {name: 'www'}

// 实现所有偶数平方的和
var sumNum = nums.filter(item => item % 2 === 0)
                 .map(item => item ** 2)
                 .reduce((pre, item) => pre + item)
  • 箭头函数的this
// 查找规则 1
var baz = () => {
    console.log(this);
}
baz.apply('aaa') // window

// 查找规则 2
var obj = {
    name:'obj',
    foo:function () {
        var bar = () => {
            console.log('bar函数',this);
        }
        return bar
    }
}
var fn = obj.foo()
fn.apply('bbb') // obj对象

面试题

// 面试题一:
var name = 'this window'

var parson = {
    name:'parson',
    sayName: function () {
        console.log(this.name);
    }
};

function sayName() {
    var sss = parson.sayName
    sss() // 独立函数调用: this window
    parson.sayName();  // 隐式绑定: parson
    (parson.sayName)(); // 隐式绑定: parson
    (b = parson.sayName)()  // 间接函数引用:this window
}
sayName()
// 面试题二:
var name = 'this window'

// 对象是没有作用域的
var person1 = {
    name: 'parson1',
    foo1: function () {
        console.log(this.name);
    },
    foo2: () => console.log(this.name),
    foo3: function () {
        return function () {
            console.log(this.name);
        }
    },
    foo4: function () {
        return () => {
            console.log(this.name);
        }
    }
};
var person2 = {name: 'parson2'}

person1.foo1() // 隐式绑定:parson1
person1.foo1.call(person2) // 显示绑定:person2

person1.foo2() // 上层作用域:this window
person1.foo2.call(person2) // 上层作用域:this window

person1.foo3()() // 独立函数调用:this window
person1.foo3.call(person2)() // 独立函数调用:this window
person1.foo3().call(person2) // 显示绑定:person2

person1.foo4()() // 上层作用域:parson1
person1.foo4.call(person2)() // 上层作用域:parson2
person1.foo4().call(person2) // 上层作用域:person1
// 面试题三
var name = 'this window'

function Person (name) {
    this.name = name
    this.foo1 = function () {
        console.log(this.name);
    }
    this.foo2 = () => console.log(this.name)
    this.foo3 = function () {
        return function () {
            console.log(this.name);
        }
    }
    this.foo4 = function () {
        return () => {
            console.log(this.name);
        }
    }
}
var person1 = new Person('person1')
var person2 = new Person('person2')

person1.foo1() // 隐式绑定:person1
person1.foo1.call(person2) // 显示绑定:person2

person1.foo2() // 上层作用域:person1
person1.foo2.call(person2) // 上层作用域:person2

person1.foo3()() // 独立函数调用:this window
person1.foo3.call(person2)() // 独立函数调用:this window
person1.foo3().call(person2) // 显示绑定:person2

person1.foo4()() // 上层作用域(隐式):parson1
person1.foo4.call(person2)() // 上层作用域(显示):parson2
person1.foo4().call(person2) // 上层作用域(隐式):person1
// 面试题四
var name = 'this window'

function Person(name) {
    this.name = name
    this.obj = {
        name: 'obj',
        foo1: function () {
            return function () {
                console.log(this.name);
            }
        },
        foo2: function () {
            return () => {
                console.log(this.name);
            }
        }
    }
}

var person1 = new Person('person1')
var person2 = new Person('person2')

person1.obj.foo1()() // 独立函数调用:this window
person1.obj.foo1.call(person2)() // 独立函数调用:this window
person1.obj.foo1().call(person2) // 显示绑定:person2

person1.obj.foo2()() // 上层作用域(隐式):obj
person1.obj.foo2.call(person2)() // 上层作用域(显示):parson2
person1.obj.foo2().call(person2) // 上层作用域(隐式):obj