默认绑定(函数直接调用)
function fn() {
console.log(this) // window
}
fn()
function fn() {
'use strict'
console.log(this) // undefined
}
fn()
var a = 1
function fn() {
var a = 2
console.log(this.a) // 1
}
fn()
// 问:把最外层 var a = 1 -> let a = 1,输出结果是?
// 答:undefined
// 因为 var 默认是全局,而let有自己的作用域不在全局。
var b = 1
function outer () {
var b = 2
function inner () {
console.log(this.b) // 1
}
inner()
}
outer()
const obj = {
a: 1,
fn: function() {
console.log(this.a)
}
}
obj.fn() // 1
const f = obj.fn; // 赋值之后,this 指向丢失,重新指向 window
f() // undefined
隐式绑定(属性访问调用)
隐式绑定的
this
指的是调用堆栈的上一级(.
前面一个)
function fn() {
console.log(this.a)
}
const obj = {
a: 1
}
obj.fn = fn
obj.fn() // 1
function fn() {
console.log(this.a)
}
const obj1 = {
a: 1,
fn
}
const obj2 = {
a: 2,
obj1
}
obj2.obj1.fn() // 1
隐式绑定失效问题:
// 第一种 是前面提过的情况
const obj1 = {
a: 1,
fn: function () {
console.log(this.a)
}
}
const fn1 = obj1.fn // 将引用给了 fn1,等同于写了 function fn1() { console.log(this.a) }
fn1() // 所以这里其实已经变成了默认绑定规则了,该函数 fn1 执行的环境就是全局环境
// 第二种 setTimeout
setTimeout(obj1.fn, 1000) // 这里执行的环境同样是全局
// 第三种 函数作为参数传递
function run(fn) {
fn()
}
run(obj1.fn) // 这里传进去的是一个引用
// 第四种 一般匿名函数也是会指向全局的
var name = 'The Window';
var obj = {
name: 'My obj',
getName: function() {
return function() { // 这是一个匿名函数
console.log(this.name)
};
}
}
obj.getName()()
// 第五种 函数赋值也会改变 this 指向
// 第六种 IIFE
显示绑定(call
、 bind
、 apply
)
function fn() {
console.log(this.a)
}
const obj = {
a: 100
}
fn.call(obj) // 100
function fn() {
console.log(this)
}
fn.bind(1)(); // 1
fn.bind(1).bind(2)(); // 1
// boxing(装箱) -> (1 ----> Number(1))
// bind 只看第一个 bind(堆栈的上下文,上一个,写的顺序来看就是第一个)
let obj = {
a: 2
}
fn.bind(obj)(); // {a: 2}
new
new 做了什么
- 创建一个空的简单 JavaScript 对象(即{})
- 链接该对象(设置该对象的constructor)到另一个对象
- 将步骤1新创建的对象作为 this 的上下文
- 如果该函数没有返回对象,则返回this
如果函数
constructor
里没有返回对象的话,this
指向的是new
之后得到的实例
function foo(a) {
this.a = a;
}
const f = new foo(2);
f.a // 2
function bar(a) {
this.a = a;
return {
a: 100
}
}
const b = new bar(3);
b.a // 100 因为如果this返回对象,那则返回对象
箭头函数
箭头函数这种情况比较特殊,编译期间确定的上下文,不会被改变,哪怕你 new
,指向的就是上一层的上下文,
箭头函数本身是没有
this
的,继承的是外层的
function fn() {
return {
b: ()=>{
console.log(this)
}
}
}
fn().b(); // window
fn().b.bind(1)(); // window
fn.bind(2)().b.bind(3)(); // 2
优先级
优先级「new 绑」 > 「显绑」 > 「隐绑」 > 「默认绑定」
// 隐式 vs 默认 -> 结论:隐式 > 默认
function fn() {
console.log(this)
}
const obj = {
fn
}
obj.fn() // {fn: ƒ}
// 显式 vs 隐式 -> 结论:显式 > 隐式
obj.fn.bind(5)() // 5
// new vs 显式 -> 结论:new > 显式
function foo (a) {
this.a = a
}
const obj1 = {}
var bar = foo.bind(obj1)
bar(2)
console.log(obj1.a) // 2
// new
var baz = new bar(3)
console.log( baz.a ) // 3
// 箭头函数没有 this,比较没有意义
习题
function foo() {
console.log(this.a) // 2
}
var a = 2;
(function () {
"use strict" // 迷惑你的
foo();
})();
var name="the window"
var object={
name:"My Object",
getName: function(){
return this.name
}
}
object.getName() // My Object
(object.getName)() // My Object // 加括号不影响
(object.getName = object.getName)() // window
(object.getName, object.getName)() // window
// 一旦有赋值,或者运算符的操作,都会丢失 this 指向
var x = 3
var obj3 = {
x: 1,
getX: function() {
var x = 5
return function() {
return this.x
}();
}
}
console.log(obj3.getX()) // 3 上文 绑定失效问题,立即执行函数指向全局
function a(x) {
this.x = x
return this
}
var x = a(5)
var y = a(6)
console.log(x.x) // undefined 其实执行的是 Number(6).x
console.log(y.x) // 6
// 等价于
window.x = 5;
window.x = window;
window.x = 6;
window.y = window;
var foo = 123;
function print() {
this.foo = 234;
console.log(foo)
};
// print(); // 234 print里面没有foo,foo在全局,this.foo改的是全局
new print() // 123 此时this指的是print,print里面的this.foo=234,但是打印的是foo,所以是全局的foo
var a = 5;
function test() {
a = 0;
console.log(a);
console.log(this.a);
var a;
console.log(a)
}
// test(); // 0 5 0 // this指向全局
new test(); // 0 undefined 0 // this指向test,this里面没有a,所以是undefined