转载请注明预见才能遇见的博客:http://my.csdn.net/
原文地址:https://blog.csdn.net/pcaxb/article/details/87622552
JavaScript设计模式系列—基础篇(二)this、call、apply和bind方法的详解和区别
目录
2.作为普通函数调用时,this 总是指向全局对象(window)
3. 构造器调用,构造器里的 this 就指向返回的这个对象
4. Function.prototype.call 或 Function.prototype.apply 调用
arguments和数组函数扩展
arguments 是一个对象,不是一个数组,是一个类数组对象。有下标,但是不能像数组一样排序或者添加一个元素。
// 打印封装
function L() {
console.log.apply(this,arguments);
}
function test(){
//1.arguments添加元素
L(arguments);//1 2
Array.prototype.push.call(arguments,3);
L(arguments);//1 2 3
//2.arguments转换成数组
var args1 = [].slice.call(arguments);
L(args1);//[1,2,3]
//es6
var args2 = Array.from(arguments);
L(args2);//[1,2,3]
// 3.删除arguments的第一个元素,返回第一个元素的值
var value = [].shift.call(arguments);
L(value)//1
// 4.数组追加
var ct = [].concat.call(args1,[2,2]);
L(ct);
}
1.this指向
JavaScript 的 this 总是指向一个对象,具体指向哪个对象是在运行时基于函数的执行环境动态绑定的,而不是函数在声明或创建时确认的。在 es5 中,this 永远指向最后调用它的那个对象。
1.当函数作为对象的方法被调用时,this 指向该对象
var person = {
age:20,
getAge:function(){
L(this === person);//true
L(this.age);//20
}
}
person.getAge()
2.作为普通函数调用时,this 总是指向全局对象(window)
var age = 20;
var getAge = function(){
L(this.age);//20
}
getAge()//等同于window.getAge()
//或者:
var age = 22;
var person = {
age:20,
getAge:function(){
L(this === person);//false
L(this.age);//22
}
}
var getAge = person.getAge;
getAge();
//或者
var age = 22;
var person = {
age:20,
getAge:function(){
L(this === person);//true
L(this.age);//20
}
}
person.getAge();//true 20
window.person.getAge();//true 20 者两个是一样的
难点:作为普通函数调用,记住匿名函数的this永远指向window
document.getElementById('div01').onclick = function(){
L(this);//div01
function callback(){
L(this);//window
}
callback();
};
//使用call来修正this
document.getElementById('div01').onclick = function(){
L(this);//div01
function callback(){
L(this);//div01
}
callback.call(this);
};
this永远指向最后调用它的那个对象,那么我们就来找最后调用匿名函数的对象,因为匿名函数名字,所以我们是没有办法被其他对象调用匿名函数的。所以说 匿名函数的this永远指向window。
var name = "window中的变量";
function fn() {
var name = 'Pcaxb';
innerFunction();
function innerFunction() {
console.log(55,this.name);
console.log(55,this);
}
innerFunction();
}
//this 永远指向最后调用它的那个对象
fn();// window中的变量
3. 构造器调用,构造器里的 this 就指向返回的这个对象
function Person(name){
this.name = name;
}
var p = new Person("cc");
L(p.name)//cc this指向p
构造函数显式的返回一个对象的情况:
function Person(name){
this.name = name;
this.age = 12;
return {
name:"aa"
}
}
var p = new Person("cc");
L(p.name)//aa p指向Person返回的对象
L(p.age)//undefined
构造函数显式的返回一个字符串的情况:
function Person(name){
this.name = name;
this.age = 12;
return ""
}
var p = new Person("cc");
L(p.name)//aa this指向p
L(p.age)//12
4. Function.prototype.call 或 Function.prototype.apply 调用
var age = 20;
var getAge = function(){
L(this.age);
}
getAge()//20
getAge.call({age:22});//22
动态地 改变传入函数的 this
5.严格模式下的this指向
function logThis1(){
L(this);//window
}
logThis1()
function logThis2(){
"use strict"
L(this);//undefined
}
logThis2()
6.with 和 eval 的情况 以后扩展
2.document.getElementById探索
var div01 = document.getElementById("div01");
L(div01);//div01
var getId = document.getElementById;
L(getId("div01"));//错误
//说明:本来getElementById函数里面的this指向document,但是getId调用的时候改变了this指向,
把getElementById函数里面的this指向改成了window
//或者
var getId = document.getElementById;
L(getId.call(document,"div01"));//div01
//或者
function getId(){
return document.getElementById.apply(document,arguments);
}
L(getId('div01'));//div01
3.改变this指向
1)使用 ES6 的箭头函数
2)在函数内部使用 _this = this
3)使用 apply、call、bind
1.使用 ES6 的箭头函数
<SCript type="text/javascript">
var name = "window中的变量";
var obj = {
name : "Pcaxb",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout( function () {
this.func1()
},100);
}
};
//obj.func2() // this.func1 is not a function
</SCript>
在不使用箭头函数的情况下,是会报错的,因为最后调用 setTimeout 的对象是 window,但是在 window 中并没有 func1 函数。
2.在函数内部使用 _this = this
<!-- 改造一 在函数内部使用 _this = this -->
<SCript type="text/javascript">
var name = "window中的变量";
var obj = {
name : "Pcaxb",
func1: function () {
console.log(11,this.name)
},
func2: function () {
var _this = this;
setTimeout( function () {
_this.func1()
},100);
}
};
obj.func2();//"Pcaxb"
</SCript>
<!-- 改造二 使用箭头函数 -->
<SCript type="text/javascript">
var name = "window中的变量";
var obj = {
name : "Pcaxb",
func1: function () {
console.log(22,this.name)
},
func2: function () {
setTimeout(()=>{
this.func1()
},100);
}
};
obj.func2();//"Pcaxb"
</SCript>
箭头函数的 this 始终指向函数定义时的 this,而非执行时。箭头函数中没有 this 绑定,必须通过查找作用域链来决定其值,如果箭头函数被非箭头函数包含,则 this 绑定的是最近一层非箭头函数的 this,否则,this 为 undefined
3.使用 apply、call、bind
<!-- 改造三 使用 apply、call、bind中的bind -->
<SCript type="text/javascript">
var name = "window中的变量";
var obj = {
name : "Pcaxb",
func1: function () {
console.log(33,this.name)
},
// func2: function () {
// setTimeout( function () {
// this.func1()
// }.bind(this),100);
// }
func2: function () {
setTimeout( function () {
this.func1()
}.bind(obj),100);
}
};
obj.func2() // Pcaxb
</SCript>
<!-- 改造四 使用 apply、call、bind中的apply -->
<SCript type="text/javascript">
var name = "window中的变量";
var obj = {
name : "Pcaxb44",
func1: function () {
console.log(44,this.name)
},
// func2: function () {
// setTimeout( function () {
// this.func1()
// }.apply(this),100);
// }
func2: function () {
setTimeout( function () {
this.func1()
}.apply(obj),100);
}
};
obj.func2() // Pcaxb
</SCript>
<!-- 改造五 使用 apply、call、bind中的call -->
<SCript type="text/javascript">
var name = "window中的变量";
var obj = {
name : "Pcaxb55",
func1: function () {
console.log(55,this.name)
},
func2: function () {
setTimeout( function () {
this.func1()
}.call(this),100);
}
// func2: function () {
// setTimeout( function () {
// this.func1()
// }.call(obj),100);
// }
};
obj.func2() // Pcaxb
</SCript>
4. apply、call、bind的使用和区别
1.call的使用规则
call接受一系列参数,第一个参数是要绑定给this的值,后面传入的是一个参数列表。当第一个参数为null、undefined的时候,默认指向window。
fun.call(thisArg, arg1, arg2, ...)
var arr = [1, 2, 3, 89, 46]
var max = Math.max.call(null, arr[0], arr[1], arr[2], arr[3], arr[4])//89
// 案例:
var obj = {
message: 'My name is: '
}
function getName(firstName, lastName) {
console.log(this.message + firstName + ' ' + lastName);
}
getName.call(obj, 'Dot', 'Dolby');//My name is: Dot Dolby
2.apply的使用规则
apply接受两个参数,第一个参数是要绑定给this的值,第二个参数是一个参数数组也可以是arguments对象或者类数组。当第一个参数为null、undefined的时候,默认指向window。
fun.apply(thisArg, [argsArray])
var arr1 = [1,2,3,89,46]
var max1 = Math.max.apply(null,arr)//89
// 案例:
var obj = {
message: 'My name is: '
}
function getName(firstName, lastName) {
console.log(this.message + firstName + ' ' + lastName)
}
getName.apply(obj, ['Dot', 'Dolby'])// My name is: Dot Dolby
3.bind的使用规则
bind接受一系列参数,和call很相似,第一个参数是this的指向,从第二个参数开始是接收的参数列表。
function.bind(thisArg[, arg1[, arg2[, ...]]])
var obj = {
name: 'Dot'
}
function printName() {
console.log(this.name)
}
var dot = printName.bind(obj)
console.log(dot) // f
dot() // Dot
bind 方法不会立即执行,而是返回一个改变了上下文 this 后的函数。而原函数 printName 中的 this 并没有被改变,依旧指向全局对象 window。
4.apply 和 call 的区别
call()方法的作用和 apply() 方法一样,区别就是call()方法接受的是参数列表(一系列的单独变量),而apply()方法接受的是一个参数数组。
function test(a,b,c){
console.log(a,b,c);
}
test.call(this,1,2,3);//1 2 3
test.apply(this,[1,2,3]);//1 2 3
当调用一个函数时,JavaScript 的解释器并不会计较形参和实参在数量、类型以及顺序上的 区别,JavaScript 的参数在内部就是用一个数组来表示的。一般情况下都是使用apply,如果明确参数个数,想一目了然的知道形参和实参关系的情况下,可以使用call来传递参数。
apply 和 call 当第一个参数为null、undefined的时候,默认指向window。
利用call和apply做继承:构造函数的继承 https://blog.csdn.net/pcaxb/article/details/100668246
5.bind 和 call 的区别
bind和call很相似,区别在于bind方法返回值是函数,我们必须手动去调用,以及bind接收的参数列表的使用。
参数使用的区别
function fn(a, b, c) {
console.log(a, b, c);
}
var fn1 = fn.bind(null, 'Dot',"Pig");
fn('A', 'B', 'C'); // A B C
fn1('A', 'B', 'C'); // Dot Pig A
fn1('B', 'C'); // Dot Pig B
fn1('B'); // Dot Pig B
fn.call(null, 'Dot'); // Dot undefined undefined
call 是把第二个及以后的参数作为 fn 方法的实参传进去,而 fn1 方法的实参实则是在 bind 中参数的基础上再往后排。
4.bind、apply、call案例
bind的使用
//案例二
function f1(a,b,c){
console.log(a,b,c);
}
f1.bind(null)(1,2,4);//1 2 4
求数组中的最大和最小值
//案例三
var arr = [1,2,3,89,46]
var max = Math.max.apply(null,arr)//89
var min = Math.min.apply(null,arr)//1
将类数组转化为数组
var trueArr = Array.prototype.slice.call(arrayLike)
使用 log 代理 console.log
function log(){
console.log.apply(console, arguments);
}
5.自己实现一个bind
Function.prototype.selfBind = function(context){
var _this = this;
return function(){
//context是传入的this对象
_this.apply(context,arguments);
}
}
var func1 = function(){
L(this);
}.bind(this);
func1();
var func2 = function(){
L(this);
}.selfBind(this);
func2();
匿名函数调用selfBind,所以selfBind里面的this是指向匿名函数的,this.apply调用的就是匿名函数,给匿名函数传context指向和参数。由于模拟bind,所以selfBind需要返回一个函数。如果匿名函数中直接使用this,这个this指向的是window,所以使用一个中间变量保存this。
bind的特性是实参实则是在 bind 中参数的基础上再往后排,代码优化
Function.prototype.selfBind = function(){
var _this = this,
//shift() 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值
context = [].shift.call(arguments);//需要绑定的 this 上下文
//slice() 方法可从已有的数组中返回选定的元素
args = [].slice.call(arguments);// 剩余的参数转成数组
console.log(context,args);
return function(){
//context是传入的this对象
_this.apply(context,[].concat.call(args,[].slice.call(arguments)));
// 执行新的函数的时候,会把之前传入的 context 当作新函数体内的 this
// 并且组合两次分别传入的参数,作为新函数的参数
}
}
var func2 = function(a,b,c,d){
L(this.name);//cc
L(a,b,c,d);//22 33 1 2
}.selfBind({name:"cc"},22,33);
func2(1,2);
参考资料:call、apply和bind方法的用法以及区别 apply、call、bind 区别 MDN说明
JavaScript设计模式系列—基础篇(二)this、call、apply和bind方法的详解和区别