this概念:
this是指当前函数,当前模块的运行环境上下文。是一个指针变量,普通函数的this是在调用时才被绑定确认指向的(即是谁调用就指向谁);
下面介绍几种this绑定规则
一、默认绑定
特点:非严格模式下 this指向全局对象(浏览器环境里指向window, node环境里指向global)
严格模式下 this指向undefined,严格模式下是不允许this指向全局对象的。
function a(){};
a(); // 函数独立调用,不带任何修饰函数的引用
var name = '哈哈哈';
var obj = {
name:'张三',
fun:function(){
// 'use strict' // 严格模式
console.log(this.name);
}
}
var p1 = obj.fun;
p1(); // 哈哈哈(严格模式下会报错Uncaught TypeError: Cannot read properties of undefined (reading 'name'))
// 这个例子是典型的默认绑定,关注点不要放在obj对象上,而要关注 函数最终调用的形式,p1的调用就是独立调用不加任何修饰的,所以指向全局window
二、隐式绑定(与默认绑定相反,函数调用前面有修饰,谁调用函数,this就指向谁)
普通函数作为参数传递的情况,setTimeout,setInterval,非严格模式下this指向全局对象
var name = '张三';
var person = {
name : '李四',
fun:fun
}
function fun(){
console.log(this.name) // 李四
// 普通函数
setTimeout(function(){
console.log(this.name) // 张三
})
// 箭头函数
setTimeout(()=>{
console.log(this.name) // 李四
})
}
person.fun();
链式调用
// 链式调用
function fun(){
console.log(this.name)
}
var p1 = {
name:"zhangsan",
fun:fun
}
var p2 = {
name:"hahah",
xixi:p1 // 关联p1
}
p2.xixi.fun(); // zhangsan // this 指向 p1(链式调用this指向保持就近原则)
三、显示绑定 call apply bind(都可修改函数的this指向)
call 和 apply 的异同点
1、都是改变this指向的
2、第一个参数都是作为this指向的,如果不传参 fun.call(),非严格模式下this会绑定到全局对象上
3、区别就是第二参数形式。call是一个一个的列表形式,apply是数组的形式
例: a.call(b,'sing','eat') a(1,['hahah','xixii'])
var person = {
name:'张三'
}
function chang_this(hob,state){
this.hob = hob;
this.state = state; // 改变this指向后相当于 person.hob = hob person.state = state
}
chang_this.call(person,'sing','eat') // 改变this指向person
console.log(person) // {name: '张三', hob: 'sing', state: 'eat'}
chang_this.apply(1,['hahah','xixii']) // 改变this指向person
console.log(person) // {name: '张三', hob: 'hahah', state: 'xixii'}
/* call和apply如果传递的第一个参数是数字或者字符串 的情况*/
function readayThisType(){
console.log(typeof this)
}
readayThisType(1); // [Number:1] object // 解析成为数字类型的对象
readayThisType('haha'); // [String: 'haha ]object // 解析成为字符串类型的对象
/* bind
1、 bind方法会创建一个新的函数
2、当这个新函数被调用时,bind的第一个参数,会作为函数运行时候的this
3、之后的 一系列参数 都会在 传递实参前 传入 作为它的参数
*/
var obj = {
name:'原有',
state:'eat',
fun : function(str){
console.log(`${str} ${this.name}`)
}
}
obj.fun('haha'); // haha 原有
var nextObj = obj.fun.bind({name:'新传', state:'run'},'xixi')
nextObj(); // xixi 新传(之前说的默认绑定指向全局是不严谨的,应该有优先级)
四、new
1.创建一个空对象
2、将空对象的__proto__指向原型对象的prototype
3、以新对象为this执行原有的构造函数
4、return
function parent(name){
this.name = name;// 通过new 将构造函数的 this 指向new出来的实例
}
var p1 = new parent('zhansan')
console.log(p1.name) // zhansan
// 通过new 将构造函数的 this 指向new出来的实例 p1
this绑定的优先级
new绑定 > 显示绑定(bind,call,apply) > 隐式绑定(obj.fun)>默认绑定(没有任何修饰,直接调用)
关于优先级 来看下面一段代码
// 测试代码
function foo(a){
this.a = a;
}
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);
// 通过new 将构造函数的 this 指向new出来的实例 bar 相当于是 function foo(){ bar.a = a;}
// 所以 下面两个打印 不会改变 obj1 对象,只会改变 实例 bar 上的属性
console.log(obj1.a);// 2
console.log(bar.a); // 4
五、箭头函数
// 验证箭头函数特性
let funs = ()=>{};
console.log(funs.prototype)// undefined // 3.箭头函数没有原型对象
console.log(funs.prototype.constructor) // 报错 Cannot read properties of undefined ();2.没有prototype 更没有prototype.constructor
// 箭头函数代码
var name = '张三';
var person = {
name : '李四',
fun:fun
}
function fun(){
console.log(this.name) // 李四
// 普通函数
setTimeout(function(){
console.log(this.name) // 张三
})
// 箭头函数
setTimeout(()=>{
console.log(arguments) // fun的参数
console.log(this.name) // 李四 在fun函数的上下文中,箭头函数的this就是fun函数的this
})
}
person.fun();
面试题:
var name = '123';
var obj = {
name:'456',
init:function(){
function a(){
console.log(this.name) // 123
}
a(); // 因为函数a和函数p都有自己的this,最终是这里调用 是默认绑定
}
}
obj.init();
//==========================================================================================
var length = 12;
function fn(){
console.log(this.length);
}
var obj = {
length :6,
fn:function(fn){
fn(); // 12
arguments[0](); // 2 分解:arguments:[fn,1] length为2
// arguments[0]就是传入的fn函数 然后再执行fn()console.log(this.length),指向调用者arguments伪数组(对象)
}
}
obj.fn(fn,2)