JavaScript中call,apply,bind的區(qū)別與實(shí)現(xiàn)
區(qū)別
- call、apply、bind相同點(diǎn):都是改變this的指向,傳入的第一個(gè)參數(shù)都是綁定this的指向,在非嚴(yán)格模式中,如果第一個(gè)參數(shù)是nul或者undefined,會(huì)把全局對(duì)象(瀏覽器是window)作為this的值,要注意的是,在嚴(yán)格模式中,null 就是 null,undefined 就是 undefined
- call和apply唯一的區(qū)別是:call傳入的是參數(shù)列表,apply傳入的是數(shù)組,也可以是類數(shù)組
- bind和call、apply的區(qū)別: bind返回的是一個(gè)改變了this指向的函數(shù),便于稍后調(diào)用,不像call和apply會(huì)立即調(diào)用;bind和call很像,傳入的也是參數(shù)列表,但是可以多次傳入,不需要像call,一次傳入
- 值得注意:當(dāng) bind 返回的函數(shù) 使用new作為構(gòu)造函數(shù)時(shí),綁定的 this 值會(huì)失效,this指向?qū)嵗龑?duì)象,但傳入的參數(shù)依然生效 (new調(diào)用的優(yōu)先級(jí) > bind調(diào)用)
call實(shí)現(xiàn)
對(duì)象context想調(diào)用一個(gè)它沒有的方法f 怎么辦呢?f.call(context) 通過call來借用方法f ,怎么做到的呢?
- 對(duì)象context添加f方法
- 對(duì)象context執(zhí)行f方法
Function.prototype.myCall = function(context, ...arg) {
// 如果第一個(gè)參數(shù)傳入的是undefined和null,context為window對(duì)象
context = context || window;
// 為context對(duì)象添加函數(shù)bar
context.fn = this; // this:bar,this指向調(diào)用myCall的bar
// context對(duì)象執(zhí)行函數(shù)bar,并返回結(jié)果
return context.fn(...arg);
}
// 測(cè)試一下
var value = 2;
var obj = {
value: 1
}
function bar(name, age) {
console.log(this.value);
return {
value: this.value,
name: name,
age: age
}
}
bar.myCall(null); // 2
console.log(bar.myCall(obj, 'kevin', 18)); //1
// Object {
// value: 1,
// name: 'kevin',
// age: 18
// }apply實(shí)現(xiàn)
apply和call唯一的區(qū)別是:call傳入的是參數(shù)列表,apply傳入的是數(shù)組,也可以是類數(shù)組
Function.prototype.myApply = function(context, arg) {
// 如果第一個(gè)參數(shù)傳入的是undefined和null,context為window對(duì)象
context = context || window;
// context對(duì)象添加函數(shù)bar
context.fn = this; // this:bar,this指向調(diào)用myCall的函數(shù)bar
// context對(duì)象執(zhí)行函數(shù)bar,并返回結(jié)果
let result = null;
if (!arg) { // 沒有傳入數(shù)組
result = context.fn();
}else{ // 傳入了參數(shù)數(shù)組
result = context.fn(...arg);
}
return result;
}
// 測(cè)試一下
var value = 2;
var obj = {
value: 1
}
function bar(name, age) {
console.log(this.value);
return {
value: this.value,
name: name,
age: age
}
}
bar.myApply(null); // 2
console.log(bar.myApply(obj, ['kevin', 18])); // 1bind實(shí)現(xiàn)
- bind和call、apply的區(qū)別: bind返回的是一個(gè)改變了this指向的函數(shù),便于稍后調(diào)用,不像call和apply會(huì)立即調(diào)用;bind和call很像,傳入的也是參數(shù)列表,但是可以多次傳入,不需要像call,一次傳入
- 值得注意:當(dāng) bind 返回的函數(shù) 使用new作為構(gòu)造函數(shù)時(shí),綁定的 this 值會(huì)失效,this指向?qū)嵗龑?duì)象,但傳入的參數(shù)依然生效 (new調(diào)用的優(yōu)先級(jí) > bind調(diào)用)
bind實(shí)現(xiàn)最為復(fù)雜,因?yàn)榻?jīng)過bind綁定過的函數(shù),既可以被當(dāng)作普通函數(shù)調(diào)用,又可以被當(dāng)作構(gòu)造函數(shù)調(diào)用
- bind 返回的函數(shù) 作為普通函數(shù)調(diào)用
// bind 返回的函數(shù) 作為普通函數(shù)調(diào)用
let bindFun = normalFun.myBind(obj, '我是參數(shù)傳進(jìn)來的name') // this:obj
bindFun('我是參數(shù)傳進(jìn)來的age')- bind 返回的函數(shù) 作為構(gòu)造函數(shù)調(diào)用,綁定的 this 值obj會(huì)失效,this指向?qū)嵗龑?duì)象a
// bind 返回的函數(shù) 作為構(gòu)造函數(shù)調(diào)用
let bindFun = Person.myBind(obj, '我是參數(shù)傳進(jìn)來的name') // this:obj
let a = new bindFun('我是參數(shù)傳進(jìn)來的age') // this:abind 返回的函數(shù) 作為普通函數(shù)調(diào)用 代碼實(shí)現(xiàn)
// bind 返回的函數(shù) 作為普通函數(shù)調(diào)用
Function.prototype.myBind = function (context, ...args){
// 如果第一個(gè)參數(shù)傳入的是undefined和null,context為window對(duì)象
context = context || window;
// context對(duì)象(obj)添加函數(shù)normalFun
context.fn = this; // this:normalFun, context.fn === normalFun,下面出現(xiàn)context.fn都可以直接看成normalFun
// bind返回的函數(shù)
return function (...innerArgs) {
// bind 返回的函數(shù) 作為普通函數(shù)被執(zhí)行
context.fn(...[...args,...innerArgs]); //相當(dāng)于normalFun(...[...args,...innerArgs])
}
}
// 測(cè)試
let obj = {
objName: '我是obj傳進(jìn)來的name',
objAge: '我是obj傳進(jìn)來的age'
}
// 普通函數(shù)
function normalFun(name, age) {
console.log(name); //'我是第一次參數(shù)傳進(jìn)來的name被args接收'
console.log(age); //'我是第二次參數(shù)傳進(jìn)來的age被innerArgs接收'
console.log(this === obj); // true,this指向obj
console.log(this.objName); //'我是obj傳進(jìn)來的name'
console.log(this.objAge); //'我是obj傳進(jìn)來的age'
}
// bind 返回的函數(shù) 作為普通函數(shù)調(diào)用
let bindFun = normalFun.myBind(obj, '我是第一次參數(shù)傳進(jìn)來的name被args接收'); // this指向obj
bindFun('我是第二次參數(shù)傳進(jìn)來的age被innerArgs接收'); bind 返回的函數(shù) 作為構(gòu)造函數(shù)調(diào)用
// bind 返回的函數(shù) 再經(jīng)過new調(diào)用
Function.prototype.myBind = function (context, ...args){
// 如果第一個(gè)參數(shù)傳入的是undefined和null,context為window對(duì)象
context = context || window;
// context對(duì)象添加函數(shù)Person
context.fn = this; // this:Person,context.fn:Person,_this:Person
let _this = this;
// bind返回的函數(shù)
const result = function (...innerArgs) {
if (this instanceof _this ) { // this:a (new出來的實(shí)例對(duì)象) , _this:Person
// 為實(shí)例對(duì)象a添加Person方法
this.fn = _this;
// 實(shí)例對(duì)象a執(zhí)行Person方法
this.fn(...[...args,...innerArgs]);
}
}
result.prototype = Object.create(this.prototype); // 為什加這一句?看原型圖下面會(huì)解釋
return result;
}
// 測(cè)試
function Person(name, age) {
console.log(name); //'我是第一次參數(shù)傳進(jìn)來的name被args接收'
console.log(age); //'我是第二次參數(shù)傳進(jìn)來的age被innerArgs接收'
console.log(this); //構(gòu)造函數(shù)this指向?qū)嵗龑?duì)象
}
// 構(gòu)造函數(shù)原型的方法
Person.prototype.say = function() {
console.log(123);
}
let obj = {
objName: '我是obj傳進(jìn)來的name',
objAge: '我是obj傳進(jìn)來的age'
}
// bind 返回的函數(shù) 作為構(gòu)造函數(shù)調(diào)用
let bindFun = Person.myBind(obj, '我是第一次參數(shù)傳進(jìn)來的name被args接收') // this:obj
let a = new bindFun('我是第二次參數(shù)傳進(jìn)來的age被innerArgs接收') // this:a
a.say() //123畫以下兩條語句的原型圖方便理解
let bindFun = Person.myBind(obj, '我是第一次參數(shù)傳進(jìn)來的name被args接收') // this:obj
let a = new bindFun('我是第二次參數(shù)傳進(jìn)來的age被innerArgs接收') // this:a當(dāng)執(zhí)行下面語句時(shí),原型圖如下:
let bindFun = Person.myBind(obj, '我是第一次參數(shù)傳進(jìn)來的name被args接收') // this:obj

當(dāng)執(zhí)行下面語句時(shí),bindFun就是result看代碼,原型圖如下:
let a = new bindFun('我是第二次參數(shù)傳進(jìn)來的age被innerArgs接收') // this:a
在這里實(shí)例對(duì)象a還需要繼承構(gòu)造函數(shù)Person的原型,所以加上了
result.prototype = Object.create(this.prototype);
原型圖最終如下:

bind代碼最終實(shí)現(xiàn)
Function.prototype.myBind = function (context, ...args){
// 如果第一個(gè)參數(shù)傳入的是undefined和null,context為window對(duì)象
context = context || window;
// context對(duì)象添加函數(shù)Person
context.fn = this; // this:Person,context.fn:Person,_this:Person
let _this = this;
// bind返回的函數(shù)
const result = function (...innerArgs) {
if (this instanceof _this ) { // this:a (new出來的實(shí)例對(duì)象) , _this:Person
// 為實(shí)例對(duì)象a添加Person方法
this.fn = _this;
// 實(shí)例對(duì)象a執(zhí)行Person方法
this.fn(...[...args,...innerArgs]);
}else{
// 普通函數(shù)被調(diào)用
context.fn(...[...args,...innerArgs]);
}
}
result.prototype = Object.create(this.prototype); // 為什加這一句?看原型圖下面會(huì)解釋
return result;
}
// 測(cè)試
// function Person(name, age) {
// console.log(name); //'我是第一次參數(shù)傳進(jìn)來的name被args接收'
// console.log(age); //'我是第二次參數(shù)傳進(jìn)來的age被innerArgs接收'
// console.log(this); //構(gòu)造函數(shù)this指向?qū)嵗龑?duì)象
// }
// // 構(gòu)造函數(shù)原型的方法
// Person.prototype.say = function() {
// console.log(123);
// }
// let obj = {
// objName: '我是obj傳進(jìn)來的name',
// objAge: '我是obj傳進(jìn)來的age'
// }
// // bind 返回的函數(shù) 作為構(gòu)造函數(shù)調(diào)用
// let bindFun = Person.myBind(obj, '我是第一次參數(shù)傳進(jìn)來的name被args接收') // this:obj
// let a = new bindFun('我是第二次參數(shù)傳進(jìn)來的age被innerArgs接收') // this:a
// a.say() //123
// 測(cè)試
let obj = {
objName: '我是obj傳進(jìn)來的name',
objAge: '我是obj傳進(jìn)來的age'
}
// 普通函數(shù)
function normalFun(name, age) {
console.log(name); //'我是第一次參數(shù)傳進(jìn)來的name'
console.log(age); //'我是第二次參數(shù)傳進(jìn)來的age'
console.log(this === obj); // true
console.log(this.objName); //'我是obj傳進(jìn)來的name'
console.log(this.objAge); //'我是obj傳進(jìn)來的age'
}
// bind 返回的函數(shù) 作為普通函數(shù)調(diào)用
let bindFun = normalFun.myBind(obj, '我是第一次參數(shù)傳進(jìn)來的name被args接收'); // this指向obj
bindFun('我是第二次參數(shù)傳進(jìn)來的age被innerArgs接收'); 到此這篇關(guān)于JavaScript中call,apply,bind的區(qū)別與實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)JS call,apply,bind區(qū)別內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
javascript實(shí)現(xiàn)下拉提示選擇框
這篇文章主要介紹了javascript實(shí)現(xiàn)下拉提示選擇框的相關(guān)資料,需要的朋友可以參考下2015-12-12
表單提交前觸發(fā)函數(shù)返回true表單才會(huì)提交
這篇文章主要介紹了表單提交前觸發(fā)函數(shù)當(dāng)返回true表單才會(huì)提交的具體實(shí)現(xiàn),需要的朋友可以參考下2014-03-03
js與jquery獲取input輸入框中的值實(shí)例講解
在本篇文章里小編給大家整理了關(guān)于js與jquery獲取input輸入框中的值實(shí)例講解,需要的朋友們可以學(xué)習(xí)參考下。2020-02-02
JavaScript Array對(duì)象擴(kuò)展indexOf()方法
JavaScript中Array對(duì)象沒有indexOf()方法,可通過下面的代碼擴(kuò)展,需要的朋友可以參考下2014-05-05
webpack中splitChunks分包策略的實(shí)現(xiàn)
splitChunks是 webpack 中用于分包的配置選項(xiàng)之一,本文主要介紹了webpack中splitChunks分包策略的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2024-06-06

