一、this
1.1 ES5中,this是在生成执行上下文时产生的,this很专一,永远指向最后调用它的对象。
1.2 this绑定的几种情况:
-
this的默认绑定---独立函数被调用。
-
this的隐式绑定---对象用
.调用方法。 -
this的显示绑定---对象使用apply()、call()、bind()三个方法。
-
this的new绑定---new方法调用构造函数。
-
匿名函数()里的this绑定到window。
二、this的绑定--this很专一,永远指向最后调用它的对象!
2.1 this的默认绑定:发生在独立函数被调用的情况下。
- 浏览器非严格模式下,一个独立的函数默认是绑定在window上的,那么,最后调用独立函数的对象就是全局对象window,this绑定到window上。而在浏览器严格模式下,this指向的是undefined。
独立函数被(window)调用this指向window
<script>
// this 的默认绑定
function fn() {
console.log(this); //Window
}
fn()
// 在浏览器中 独立函数调用,函数内部this表示window
</script>
<script>
function fn() {
console.log(this); //window
}
function gn() {
console.log(this); //window
fn(); // 独立函数调用
}
function kn() {
console.log(this); //window
gn(); // 独立函数调用
}
kn(); // 独立函数调
</script>
<script>
let obj = {
name: 'wc',
fn: function() {
console.log(this); //window
}
}
let gn = obj.fn;
gn(); // 独立函数调用
</script>
<script>
function fn() {
console.log(this); //Window
}
var obj = {
name: 'wc',
fn: fn
}
var gn = obj.fn;
gn();
</script>
<script>
function fn() {
function gn() {
console.log(this);
}
return gn; //返回一个地址 指向堆里的独立函数
}
let kn = fn(); //用地址赋值
kn(); // 独立函数调用
</script>
2.2 this的隐式绑定: 发生在对象用.调用方法的情况下。
- this绑定到调用(此时也属于最后调用)方法的对象上。例如,对象a的方法b用
.进行调用,形如:window.a.b();,此时最后调用方法b的就是这个对象a,此时this绑定到a。
以方法的形式调用时,this指向调用方法的对象
<script>
// this 的隐式绑定
function fn() {
console.log(this); //{name: 'wc', fn: ƒ}
}
let obj = {
name: 'wc',
fn: fn
}
obj.fn(); //不是独立函数的调用
// 是通过obj打点去调用
// fn中的this 表示的是 调用者obj
</script>
<script>
var obj = {
name: 'wc',
running: function() {
console.log(this.name + '在跑步...'); //wc在跑步...
},
coding: function() {
console.log(this.name + '在敲代码...'); //wc在敲代码...
}
}
obj.running(); //this的 隐式绑定
obj.coding(); //this的 隐式绑定
// obj调其用方法 this指向obj
</script>
<script>
let obj = {
name: 'wc',
fn: function() {
console.log(this); //{name: 'xq', gn: ƒ}
}
};
let obj2 = {
name: 'xq',
gn: obj.fn
};
obj2.gn(); //隐式绑定
</script>
2.3 this的显示绑定: 发生现在对象使用apply()、call()、bind()三个方法的情况。
- apply()、call()、bind()三个方法的使用得到的结果略有不同,但是他们仨都有一个指定的this值。例如,apply(A,[参数,/])、bind(B,参数,/)、call(C,参数,/),此时指定的this值分别是A、B、C。call() 和 apply() 是预定义的函数方法。 两个方法可用于调用函数,两个方法的第一个参数必须是对象本身,bind()方法不可以直接调用函数,需要赋值到一个变量上,手动调用。
<script>
// this 的显示绑定
// 在JS中函数有多重角色
// 1)普通函数
// 2)对象中的方法
// 3)对象(JS中一切皆为对象Object) 函数也是对象,对象是属性的无序集合,内部有非常多的默认属性和方法。
// 4) 函数也是类(构造函数/构造器),通常情况下首字母大写
//要掌握三个方法: call() apply() bind()
function fn() {
console.log('fn....');
}
fn(); //'fn....'
let obj = {
// 对象中的函数称为方法
running: function() {
console.log('running...');
}
}
function gn() {
console.log('gn...');
}
gn.a = 1;
gn.b = 2;
gn.c = 3;
console.log(gn.a); //1
console.log(gn.b); //2
console.log(gn.c); //3
console.dir(gn); //ƒ gn(){a = 1,b = 2,c = 3} //以对象的方式打印函数 可以查看函数(对象)内部的属性
</script>
控制台输出见下图:
call()方法下this的绑定
<script>
function fn(num1, num2) {
console.log(this, num1 + num2);
}
let obj = {
name: 'wc'
}
// 函数也是对象,call()是其内部的一个方法
// 此方法可以让我们显示绑定this
fn.call(obj); // {name: 'wc'} NaN
//将fn的this 显示绑定到obj上,并执行
fn.call(obj, 666, 111); //{name: 'wc'} 777
// call传参:从第二个参数起,传递参数给函数
fn(1, 2); //Window 3
</script>
// call()的作用:
// 1) 显示绑定this
// 2) 让fn执行
// 3) 可以传参
apply()方法下this的绑定
<script>
function fn(num1, num2) {
console.log(this, num1 + num2);
}
let obj = {
name: 'wc'
}
// apply()方法示绑定this与call()方法相似,细微差别在apply()以[数组]的形式传参
// 此方法可以让我们显示绑定this
fn.apply(obj); // {name: 'wc'} NaN
//将fn的this 显示绑定到obj上,并执行
fn.apply(obj, [666, 111]); //{name: 'wc'} 777 apply()与call()方法差别,参数是以数组类型传入
// call传参:从第二个参数起,传递参数给函数
fn(1, 2); //Window 3
</script>
// apply()的作用:
// 1) 显示绑定this
// 2) 让fn执行
// 3) 可以传参[以数组形式]
apply()方法下this的绑定
<script>
function fn(num1, num2) {
console.log(this, num1 + num2);
}
let obj = {
name: 'wc'
}
let newFn = fn.bind(obj, 666, 111);
newFn();//{name: 'wc'} 777 手动调用
</script>
// bind()作用:
// 1) 显示绑定this
// 2) 可以传参 但不会让函数执行 call()、apply()会让函数执行,bind()会返回一个绑定了this的新函数
// 3) bind()返回绑定this之后的新函数,需手动调用
String 、Number、undefined、null 原始数据类型作为this绑定对象的情况
<script>
function fn() {
console.log(this);
}
//当用显示绑定方法,把this绑定到一个字符串(String)上时:会把该字符串包装成一个String{}形式的对象
fn.call('hello'); //String {'hello'}
fn.apply('hello'); //String {'hello'}
let b1 = fn.bind('hello');
b1(); //String {'hello'}
// String {'hello'}是一个对象
//
//当用显示绑定方法,把this绑定到一个数值(Number)上时:会把该数值包装成一个Number{}形式的对象
fn.call(1); // Number {1}
fn.apply(1); // Number {1}
let b2 = fn.bind(1);
b2(); // Number {1}
//
//当用显示绑定方法,把this绑定到undefined上时:实际是绑到window上
fn.call(undefined); //Window{}
fn.apply(undefined); //Window{}
let b3 = fn.bind(undefined);
b3(); //Window{}
//
//当用显示绑定方法,把this绑定到null上时:实际是绑到window上
fn.call(null); //Window{}
fn.apply(null); //Window{}
let b4 = fn.bind(null);
b4(); //Window{}
//
//控制台打印见下图↓
</script>
显示绑定总结
示绑定总结:
1) 对象call()方法 fn.call(obj,1,2) 显示绑定this 到obj上 让fn执行 能传参。
2)对象apply()方法 fn.apply(obj,[1,2]) 显示绑定this 到obj上 让fn执行 能传参[以数组形式]。
3)对象bind()方法 fn.bind(obj,1,2) 显示绑定this 到obj上 不能让fn直接执行 而是返回一个新函数 能传参。
4)当用显示绑定方法,把this绑定到一个 字符串(String)上时:会把该字符串包装成一个String{}形式的对象。
5)当用显示绑定方法,把this绑定到一个 数值(Number)上时:会把该数值包装成一个Number{}形式的对象。
6)当用显示绑定方法,把this绑定到undefined上时:实际是绑到window上。
7)当用显示绑定方法,把this绑定到null上时:实际是绑到window上。
2.4this的new绑定: 发生在new方法调用构造函数的情况。
- new方法会构造一个全新的对象,this绑定到构造出来全新的对象上。
<script> // 使用构造函数调用函数 // 如果函数调用前使用了 new 关键字, 则是调用了构造函数。 // 这看起来就像创建了新的函数, 但实际上 JavaScript 函数是重新创建的对象: // 构造函数: function myFunction(arg1, arg2) { this.firstName = arg1; this.lastName = arg2; this.age = 18 } // This creates a new object var a = new myFunction("Li", "Cherry"); console.log(a.lastName); // 返回 "Cherry" console.log(a.age); //18 </script><script> // 这就要说另一个面试经典问题:new 的过程了 // 这里就简单的来看一下 new 的过程吧: // 伪代码表示: function myFunction(arg1, arg2) { this.firstName = arg1; this.lastName = arg2; this.age = 18 } var a = new myFunction("Li", "Cherry"); new myFunction { var obj = {age = 18};// new方法创建的新对象 obj.__proto__ = myFunction.prototype; var result = myFunction.call(obj, "Li", "Cherry"); return typeof result === 'obj' ? result : obj; } // 创建一个空对象 obj; // 将新创建的空对象的隐式原型指向其构造函数的显示原型。 // 使用 call 改变 this 的指向 // 如果无返回值或者返回一个非对象值, 则将 obj 返回作为新对象; 如果返回值是一个新对象的话那么直接直接返回该对象。 // 所以我们可以看到, 在 new 的过程中, 我们是使用 call 改变了 this 的指向。 </script>
2.5 匿名函数里的this绑定。
2.5.1 JS中的匿名函数:
定义:匿名函数顾名思义指的是没有名字的函数。
形如:
function (arguments){
console.log("我是匿名函数");//此时没有调用函数,所以不会执行函数体内的代码
}
2.5.2 匿名函数表达式:
函数名是函数(此函数是所有类型函数的总称包括匿名函数、箭头函数等,函数也是对象)内部的私有属性,是与生俱来的,默认情况下是空字符串“”,在使用function声明函数时,从:想个适合的名称到落实到敲代码这个过程,其实是对函数name属性赋值的过程。
匿名函数的name属性是空字符串“”,当需要使用()来调用匿名函数时,需要将其转换成函数表达式的形式。
将匿名函数放在小括号()里,使其成为函数表达式,形如:(function(){})或用等号赋值的形式nm=function(){},再用()放在函数表达式后面调用,形如:(function(){})()或nm(),亦或采用setTimeout()等其他形式回调,此时的匿名函数的里面的this永远指向window。
<script>
console.log('------------------------一、函数内部属性------------------------------------');
console.log('------1.1 普通函数↓------');
// 声明普通函数
function fn() {};
console.dir(fn);
console.log('------1.2 匿名函数↓------');
// 匿名函数
console.dir(function() {});
console.log('------1.3 箭头函数↓------');
// 箭头函数
console.dir(() => {});
console.log('------------------------二、匿名函数调用------------------------------------');
console.log('------2.1 函数表达式A型↓------');
let nm = function() {
console.log('我是个匿名函数表达式,我被用()调用啦!');
}
console.dir(nm);
// ()调用匿名函数表达式A型
nm(); //我是个匿名函数表达式,我被用()调用啦!
console.log('------2.2 函数表达式B型↓------');
// ()立即调用匿名函数表达式B型
(function() {
console.log('我是个被()调用的立即执行函数表达式,大家都叫我立即执行函数IIFE。');
})(); //我是个被()调用的立即执行函数表达式,大家都叫我立即执行函数IIFE。
</script>
控制台输出:↓
(function(arguments){
console.log("我在调用匿名函数表达式")
})();
此时调用的函数表达式 也称为:立即执行的函数表达式(IIFE)。
2.5.3 匿名函数表达式内部的this绑定
简单的事件绑定,this指向事件源,但使用定时器setTimeout()或setInterval()回调匿名函数表达式,表达式内部的this指向window。不要忘记this永远指向最后调用它的对象。
<body>
<button id="btn">点击</button>
</body>
<script>
var a = 0;
//获取元素
var btn = document.querySelector("#btn");
//绑定事件
btn.onclick=function(){ //简单绑定事件中的匿名函数,事件绑定最好不要理解成调用匿名函数表达式
console.log(this); //<button id="btn">点我</button>
}
btn.addEventListener('click',function (){
setTimeout(function(){ // setTimeout()回调匿名函数表达式 this指向window
console.log(this.a); //0
},2000);
})
</script>
<script>
var a = 0;
var fn = function() {
return this.a;
}
//()调用匿名函数表达式 this指向window
console.log(fn()); // 0
setTimeout(function() {
console.log(this.a); //0 定时器回调匿名函数表达式 this指向window
}, 2000);
</script>
<script>
//将匿名函数作为返回值
var a = 0; //使用let不会声明不会挂在window上
function fn() {
return function() {
let a = 1;
this.a = "禁止套娃";
console.log(a);
return this.a;
}
}
//调用匿名函数表达式
console.log(fn()()); //禁止套娃
console.log('------------------------------------');
//或
var box = fn();
console.log(box()); //禁止套娃
console.log(a); //禁止套娃
</script>
易混淆的匿名函数内部this的指向
- 匿名函数作为对象方法,被用
.调用,此时是隐式绑定,this指向最后调用它的对象
<script>
var obj={
name:"wc",
age:100,
fn:function(){
return this.name+"今年"+this.age+"岁!";
}
};
console.log(obj.fn());// 用括号()调用匿名函数fn // wc今年100岁!
</script>
记住: this永远指向最后调用它的对象!
三、this绑定改的优先级
- 默认绑定(独立函数调用) 最低
- 显示绑定优先级 > 隐式绑定
- new 绑定优先级 > 隐式绑定
- new 绑定优先级 > bind(显示绑定)
3.1显示绑定优先级高于隐式绑定
script>
// 显示绑定优先级高于隐式绑定
function fn() {
console.log(this);
}
let obj = {
name: 'wc',
// 显示绑定 优先级高
fn: fn.bind({
name: 'xq'
})
}
// 隐式绑定
obj.fn(); //{name: 'xq'}
</script>
3.2显示绑定优先级高于隐式绑定
<script>
// new优先级高于隐式绑定
let obj = {
name: 'wc',
fn: function() {
console.log(this);
}
}
//
let res = new obj.fn(); //fn {} 构造的新对象
</script>
3.3 new 绑定优先级 > bind(显示绑定)
<script>
// new绑定优先级高于bind()显示绑定
function fn() {
console.log(this);
}
// bind 返回一个绑定this之后的对象
let gn = fn.bind({
name: 'wc'
})
gn(); //{name: 'wc'}
new gn(); //fn {} 新对象,
</script>
四、箭头函数中的this
我们先记住箭头函数的两个特性:
-
箭头函数的this不指向箭头函数本身,而是指向它的上一层。
-
箭头函数使用call()方法修改this指向无效。
<script>
// 箭头函数中的this 需要往上找一层
var gn = () => {
console.log(this);
}
var obj = {
name: 'wc'
}
// 显示绑定
gn.call(obj) //window 此时call方法无效
</script>
<script>
let fn = () => {
console.log(this);
}
fn.call('hello'); //Window
fn.call({}); //Window
</script>
<script>
var obj = {
name: 'wc',
fn: () => {
console.log(this);
}
}
obj.fn(); //window
//如不是箭头函数 就实现this隐式绑定,this指向obj
</script>
<script>
setTimeout(() => {
console.log(this);
}, 2000); //Window
</script>