js的this揭秘
this在全局作用域的指向
// this一般出现在函数中
// 全局作用域中:
// 浏览器: this就是window
// Node环境: this为{}
console.log(this);
// console.log(window); // Node里面没有window对象
/**
* 关于Node中this为{}的解释:
* node会将每一个js文件当作一个module模块
* module->加载->编译->放在一个函数中->执行这个函数->.call()绑定this.(node模板)
* this在绑定之前就是一个{}.
*/
调用同一个函数this的差别
// this的指向, 跟函数所处的位置是没有关系的
// 跟函数被调用者有关系 (被谁调用)
function foo() {
console.log(this)
}
// 1.直接调用这个函数
foo()
// 2.创建一个对象, 对象中的函数指向foo
var obj = {
name: 'Fhup',
foo: foo
}
obj.foo()
// 3.apply调用
foo.apply("hao")
四种绑定规则
绑定规则一-默认绑定
// 默认绑定: 独立函数调用. this就是window 注意: 有没有调用主题,没有就是window
// 案例1
// function foo1() {
// console.log(this)
// }
// function foo2() {
// console.log(this)
// foo1()
// }
// function foo3() {
// console.log(this)
// foo2()
// }
// foo3()
// 案例2
// var obj = {
// name: "why",
// foo: function() {
// console.log(this)
// }
// }
// var bar = obj.foo
// bar() // window
// 案例3
// function foo() {
// console.log(this)
// }
// var obj = {
// name: "why",
// foo: foo
// }
// var bar = obj.foo
// bar() // window
// 5.案例五:
function foo() {
function bar() {
console.log(this)
}
return bar
}
var fn = foo()
fn() // window
// 以上都是独立函数调用
绑定规则二-隐式绑定
// 隐式绑定: object.fn()
// object对象会被js引擎绑定到fn函数中的this里面
// 案例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 + "在跑步")
// }
// }
// obj.eating()
// obj.running()
// var fn = obj.eating
// fn() // window
// 案例3
var obj1 = {
name: "obj1",
foo: function() {
console.log(this)
}
}
var obj2 = {
name: "obj2",
bar: obj1.foo
}
obj2.bar()
绑定规则三-显式绑定-apply-call-bind
// 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]) // 剩余参数放在数组中
// call和apply在执行函数时,可以明确的绑定this, 这个绑定规则称之为显示绑定
function foo() {
console.log(this)
}
// 默认绑定和显示绑定bind冲突: 优先级(显示绑定)
var newFoo = foo.bind("aaa") // 绑定完,生成新的函数.
newFoo() // 显式绑定>默认(独立函数) aaa
newFoo()
newFoo()
绑定规则四-new绑定
// 我们通过一个new关键字调用一个函数时, 这个时候this是在调用这个函数构造器时创建出来的对象
// this = 创建出来的对象, this绑定在创建出来的对象上
// 这个绑定过程就是 new绑定
// new Person() 执行时,把函数当作构造器.就会执行一次函数
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)
规则优先级
/**
* 默认绑定的优先级 最低,独立函数调用
* 显式绑定大于隐式绑定 call/apply/bind > obj.fn()
* bind优先级 > call/apply
* new绑定大于隐式绑定 new > obj.fn()
* new绑定的优先级最高 > 显式绑定 结论: new关键字和apply/call不能一起使用
*
*
* new绑定 > 显示绑定(apply/call/bind) > 隐式绑定(obj.fn()) > 默认绑定(独立函数调用)
*/
二个特殊的优先级
忽略显式绑定-特殊
function foo() {
console.log(this);
}
foo.apply('ccc') // ccc
foo.apply({}) // {}
// apply/call/bind: 传入null/undefined时,自动将this绑定为全局对象window
foo.apply(null) // window
foo.apply(undefined) // window
var fn = foo.bind(null)
fn() // 显式>默认 window
间接函数引用-特殊
var obj1 = {
foo: function foo(){
console.log(this);
}
}
var obj2 = {
name:'Fhup'
}; // 注意: 加上 ; ,特殊的写法执行时会在一行
// obj2.fn = obj1.foo
// obj2.fn() // obj2
// (obj2.fn = obj1.foo)() 括号里面的当作整体,返回foo,进行执行
(obj2.fn = obj1.foo)() // window 这种相当于独立函数调用(但不是独立函数调用)
箭头函数的this
/**
* 箭头函数是ES6的新方法
* 1.箭头函数不会绑定this.arauments
* 2.不能当做构造器来使用(不能与new同时使用)
*/
var foo = (n1, n2) => {
return n1 + n2
}
foo(1, 2)
/**
* 简写:
* 1. 一个参数 ()省略
* 2. 一行代码 {}省略 并且将这一行代码的结果作为返回值返回
* 3. 返回值为对象时 加上()
*/
var bar = () => {
return { name: 'Fhup', age: 18 }
}
// 返回值为对象时 加上()
var xxx = ()=>({ name: 'Fhup', age: 18 })
// 箭头函数不绑定this,去上层作用域里面找
// 结论: 定义的对象不产生作用域,所以foo2的上层作用域是全局
// 定义的函数产生作用域,去当前函数外的对象里面找
// 1.测试箭头函数中this指向
var name = "why"
var foo = () => {
console.log(this)
}
foo() // this为 window
var obj = { foo: foo }
obj.foo() // this为 window
foo.call("abc") // this为 window
// 2.应用场景
var obj = {
data: [],
getData: function() {
// 发送网络请求, 将结果放到上面data属性中
// 在箭头函数之前的解决方案
// var _this = this
// setTimeout(function() {
// var result = ["abc", "cba", "nba"]
// _this.data = result
// }, 1000);
// 箭头函数之后
setTimeout(() => {
var result = ["abc", "cba", "nba"]
this.data = result
console.log(this.data);
}, 1000);
}
}
obj.getData()
this面试题
var name = "window";
var person = {
name: "person",
sayName: function () {
console.log(this.name);
}
};
function sayName() {
var sss = person.sayName;
sss(); // window: 独立函数调用
person.sayName(); // person: 隐式调用
(person.sayName)(); // person: 隐式调用
(b = person.sayName)(); // window: 赋值表达式(独立函数调用) 前面括号返回sayName进行执行
console.log(this.name); // window
}
sayName();
var name = 'window'
// 定义对象不产生作用域,所以foo2的上层作用域是全局
// 如果上层作用域为函数,看当前是谁调用的,或者this是定义的对象
var person1 = {
name: 'person1',
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: 'person2' }
// person1.foo1(); // person1(隐式绑定)
// person1.foo1.call(person2); // person2(显示绑定优先级大于隐式绑定)
// person1.foo2(); // window(不绑定作用域,上层作用域是全局)
// person1.foo2.call(person2); // window
// person1.foo3()(); // window(独立函数调用) person1.foo3()是返回的结果,没有调用主题
// person1.foo3.call(person2)(); // window(独立函数调用)
// person1.foo3().call(person2); // person2(最终调用返回函数式, 使用的是显示绑定)
person1.foo4()(); // person1 (箭头函数不绑定this, 上层作用域是foo4()的this是person1)
person1.foo4.call(person2)(); // person2(上层作用域被显示的绑定了一个person2)
person1.foo4().call(person2); // person1 (箭头函数不绑定this, 上层作用域是foo4()的this是person1)
var name = '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 (上层作用域中的this是person1)
// person1.foo2.call(person2) // person1 (上层作用域中的this是person1)
// person1.foo3()() // window(独立函数调用)
// person1.foo3.call(person2)() // window
// person1.foo3().call(person2) // person2
person1.foo4()() // person1
person1.foo4.call(person2)() // person2
person1.foo4().call(person2) // person1
var name = 'window'
function Person (name) {
this.name = name
this.obj = {
name: 'obj',
foo1: function () {
return function () {
console.log(this.name)
}
},
foo2: function () {
// foo2被obj调用.this就是obj
return () => {
console.log(this.name)
}
}
}
}
var person1 = new Person('person1')
var person2 = new Person('person2')
// person1.obj.foo1()() // window(独立函数调用)
// person1.obj.foo1.call(person2)() // window
// person1.obj.foo1().call(person2) // person2
person1.obj.foo2()() // obj (箭头函数不绑定this, 上层作用域是foo2()的this是obj)
person1.obj.foo2.call(person2)() // person2
person1.obj.foo2().call(person2) // obj (箭头函数不绑定this, 上层作用域是foo2()的this是obj)
var obj = { // { } 是个对象,不包含作用域
name: "obj",
foo: function() {
// 上层作用域是全局
}
}
// 函数有作用域
function Student() {
// Student上层作用域为全局
this.foo = function() {
// 上层作用域是Student
}
}
var obj = { // 定义对象不产生作用域
name: "obj",
foo: function() { // foo的上层作用域为全局
return ()=>{ //箭头函数的this就是foo的this.
console.log(this.name); // name
}
}
}