区别

  • call、apply、bind相同点:都是改变this的指向,传入的第一个参数都是绑定this的指向,在非严格模式中,如果第一个参数是nul或者undefined,会把全局对象(浏览器是window)作为this的值,要注意的是,在严格模式中,null 就是 null,undefined 就是 undefined
  • call和apply唯一的区别是:call传入的是参数列表,apply传入的是数组,也可以是类数组
  • bind和call、apply的区别: bind返回的是一个改变了this指向的函数,便于稍后调用,不像call和apply会立即调用;bind和call很像,传入的也是参数列表,但是可以多次传入,不需要像call,一次传入
  • 值得注意:当 bind 返回的函数 使用new作为构造函数时,绑定的 this 值会失效,this指向实例对象,但传入的参数依然生效 (new调用的优先级 > bind调用)

call实现

对象context想调用一个它没有的方法f 怎么办呢?f.call(context) 通过call来借用方法f ,怎么做到的呢?

  • 对象context添加f方法
  • 对象context执行f方法
Function.prototype.myCall = function(context, ...arg) {
    // 如果第一个参数传入的是undefined和null,context为window对象
    context = context || window;  
    
    // 为context对象添加函数bar
    context.fn = this;   // this:bar,this指向调用myCall的bar  

    // context对象执行函数bar,并返回结果
    return  context.fn(...arg); 
}

// 测试一下
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实现

apply和call唯一的区别是:call传入的是参数列表,apply传入的是数组,也可以是类数组

Function.prototype.myApply = function(context, arg) {
    // 如果第一个参数传入的是undefined和null,context为window对象
    context = context || window;  
    
    // context对象添加函数bar
    context.fn = this;   // this:bar,this指向调用myCall的函数bar  

    // context对象执行函数bar,并返回结果
    let result = null;
    if (!arg) { // 没有传入数组
        result = context.fn();
    }else{      // 传入了参数数组
        result = context.fn(...arg);
    } 
    return result;
}

// 测试一下
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])); // 1

bind实现

  • bind和call、apply的区别: bind返回的是一个改变了this指向的函数,便于稍后调用,不像call和apply会立即调用;bind和call很像,传入的也是参数列表,但是可以多次传入,不需要像call,一次传入
  • 值得注意:当 bind 返回的函数 使用new作为构造函数时,绑定的 this 值会失效,this指向实例对象,但传入的参数依然生效 (new调用的优先级 > bind调用)

bind实现最为复杂,因为经过bind绑定过的函数,既可以被当作普通函数调用,又可以被当作构造函数调用

  • bind 返回的函数 作为普通函数调用
// bind 返回的函数 作为普通函数调用
let bindFun = normalFun.myBind(obj, '我是参数传进来的name') // this:obj
bindFun('我是参数传进来的age')
  • bind 返回的函数 作为构造函数调用,绑定的 this 值obj会失效,this指向实例对象a
// bind 返回的函数 作为构造函数调用
let bindFun = Person.myBind(obj, '我是参数传进来的name') // this:obj
let a = new bindFun('我是参数传进来的age')               // this:a

bind 返回的函数 作为普通函数调用 代码实现

// bind 返回的函数 作为普通函数调用
Function.prototype.myBind = function (context, ...args){
    // 如果第一个参数传入的是undefined和null,context为window对象
    context = context || window;

    // context对象(obj)添加函数normalFun
    context.fn = this;  // this:normalFun, context.fn === normalFun,下面出现context.fn都可以直接看成normalFun
    
    // bind返回的函数  
    return function (...innerArgs) {
        // bind 返回的函数 作为普通函数被执行
        context.fn(...[...args,...innerArgs]);  //相当于normalFun(...[...args,...innerArgs])
    }
}

// 测试
let obj = {
  objName: '我是obj传进来的name',
  objAge: '我是obj传进来的age'
}
// 普通函数
function normalFun(name, age) {
  console.log(name);          //'我是第一次参数传进来的name被args接收'
  console.log(age);           //'我是第二次参数传进来的age被innerArgs接收'
  console.log(this === obj);  // true,this指向obj
  console.log(this.objName);  //'我是obj传进来的name'
  console.log(this.objAge);   //'我是obj传进来的age'
}

// bind 返回的函数 作为普通函数调用
let bindFun = normalFun.myBind(obj, '我是第一次参数传进来的name被args接收'); // this指向obj 
bindFun('我是第二次参数传进来的age被innerArgs接收'); 

bind 返回的函数 作为构造函数调用

// bind 返回的函数 再经过new调用  
Function.prototype.myBind = function (context, ...args){
    // 如果第一个参数传入的是undefined和null,context为window对象
    context = context || window;

    // context对象添加函数Person
    context.fn = this;     // this:Person,context.fn:Person,_this:Person
    let _this = this;

    // bind返回的函数 
    const result = function (...innerArgs) { 
        if (this instanceof _this ) { // this:a (new出来的实例对象) ,  _this:Person
            // 为实例对象a添加Person方法
            this.fn = _this;
            // 实例对象a执行Person方法
            this.fn(...[...args,...innerArgs]);
        }
    }
    result.prototype = Object.create(this.prototype);  // 为什加这一句?看原型图下面会解释
    return result;
}
// 测试
function Person(name, age) {
  console.log(name); //'我是第一次参数传进来的name被args接收'
  console.log(age); //'我是第二次参数传进来的age被innerArgs接收'
  console.log(this); //构造函数this指向实例对象
}
// 构造函数原型的方法
Person.prototype.say = function() {
  console.log(123);
}

let obj = {
  objName: '我是obj传进来的name',
  objAge: '我是obj传进来的age'
}

// bind 返回的函数 作为构造函数调用
let bindFun = Person.myBind(obj, '我是第一次参数传进来的name被args接收') // this:obj
let a = new bindFun('我是第二次参数传进来的age被innerArgs接收')               // this:a
a.say() //123

画以下两条语句的原型图方便理解

let bindFun = Person.myBind(obj, '我是第一次参数传进来的name被args接收') // this:obj
let a = new bindFun('我是第二次参数传进来的age被innerArgs接收')               // this:a

当执行下面语句时,原型图如下:

let bindFun = Person.myBind(obj, '我是第一次参数传进来的name被args接收') // this:obj

当执行下面语句时,bindFun就是result看代码,原型图如下:

let a = new bindFun('我是第二次参数传进来的age被innerArgs接收')               // this:a

在这里实例对象a还需要继承构造函数Person的原型,所以加上了

result.prototype = Object.create(this.prototype);

原型图最终如下:

bind代码最终实现

Function.prototype.myBind = function (context, ...args){
    // 如果第一个参数传入的是undefined和null,context为window对象
    context = context || window;

    // context对象添加函数Person
    context.fn = this;     // this:Person,context.fn:Person,_this:Person
    let _this = this;
    
    // bind返回的函数 
    const result = function (...innerArgs) { 
        if (this instanceof _this ) { // this:a (new出来的实例对象) ,  _this:Person
            // 为实例对象a添加Person方法
            this.fn = _this;
            // 实例对象a执行Person方法
            this.fn(...[...args,...innerArgs]);
        }else{
            // 普通函数被调用
            context.fn(...[...args,...innerArgs]);
        }
    }
    
    result.prototype = Object.create(this.prototype);  // 为什加这一句?看原型图下面会解释
    return result;
}

// 测试
// function Person(name, age) {
//   console.log(name); //'我是第一次参数传进来的name被args接收'
//   console.log(age); //'我是第二次参数传进来的age被innerArgs接收'
//   console.log(this); //构造函数this指向实例对象
// }
// // 构造函数原型的方法
// Person.prototype.say = function() {
//   console.log(123);
// }

// let obj = {
//   objName: '我是obj传进来的name',
//   objAge: '我是obj传进来的age'
// }

// // bind 返回的函数 作为构造函数调用
// let bindFun = Person.myBind(obj, '我是第一次参数传进来的name被args接收') // this:obj
// let a = new bindFun('我是第二次参数传进来的age被innerArgs接收')               // this:a
// a.say() //123

// 测试
let obj = {
  objName: '我是obj传进来的name',
  objAge: '我是obj传进来的age'
}

// 普通函数
function normalFun(name, age) {
  console.log(name);          //'我是第一次参数传进来的name'
  console.log(age);           //'我是第二次参数传进来的age'
  console.log(this === obj);  // true
  console.log(this.objName);  //'我是obj传进来的name'
  console.log(this.objAge);   //'我是obj传进来的age'
}

// bind 返回的函数 作为普通函数调用
let bindFun = normalFun.myBind(obj, '我是第一次参数传进来的name被args接收'); // this指向obj 
bindFun('我是第二次参数传进来的age被innerArgs接收');  

到此这篇关于JavaScript中call,apply,bind的区别与实现的文章就介绍到这了,更多相关JS call,apply,bind区别内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

JavaScript中call,apply,bind的区别与实现的更多相关文章

  1. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  2. HTML5数字输入仅接受整数的实现代码

    这篇文章主要介绍了HTML5数字输入仅接受整数的实现代码,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  3. amaze ui 的使用详细教程

    这篇文章主要介绍了amaze ui 的使用详细教程,本文通过多种方法给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  4. html5简介_动力节点Java学院整理

    这篇文章主要介绍了html5简介,用于指定构建网页的元素,这些元素中的大多数都用于描述网页内容,有兴趣的可以了解一下

  5. ios 8 Homescreen webapp,关闭和打开iPad停止javascript

    我有一个适用于iPad的全屏HTML5网络应用程序,并且刚刚安装了IOS8来试用它,它一切正常,直到你关闭并重新启动iPad.一旦web应用程序重新启动javascript就会停止并加载新页面不会重新启动它.在iPad上的Safari中打开同一页面时,关闭和打开iPad会继续按预期工作.其他人注意到了这个或想出了一个解决方案吗?解决方法这似乎是我在iOS8.1.1更新中解决的.

  6. iOS 6 javascript与object.defineProperty的间歇性问题

    当访问使用较新的Object.defineProperty语法定义属性的对象的属性时,有没有其他人注意到新iOS6javascript引擎中的间歇性错误/问题?https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/defineProperty我正在看到javascript失败的情况,说

  7. ios – 如何使用JSExport导出内部类的方法

    解决方法似乎没有办法将内部类函数导出到javascript.我将内部类移出并创建了独立的类,它起作用了.

  8. 静音iOS推送通知与React Native应用程序在后台

    我有一个ReactNative应用程序,我试图获得一个发送到JavaScript处理程序的静默iOS推送通知.我看到的行为是AppDelegate中的didReceiveRemoteNotification函数被调用,但是我的JavaScript中的处理程序不会被调用,除非应用程序在前台,或者最近才被关闭.我很困惑的事情显然是应用程序正在被唤醒,并且它的didReceiveRemoteNotifi

  9. ios – 内存泄漏与UIWebView和Javascript

    清楚地包含一个Javascript文件到我的HTML是使UIWebView泄漏内存.当我重复使用相同的UIWebView对象时,或者每当我有内容实例化一个新的漏洞时,会出现泄漏的事实,导致我认为必须有一些JavaScript文件被loadHTMLString处理,导致泄漏.有人知道如何解决这个问题吗?

  10. iOS应用程序的UI自动化测试如何与乐器和Javascript

    从WWDC2010视频会议中了解iOS应用程序的自动化UI测试,但没有实践.从代码项目project,我们可以有一个例子.这个问题在这里听到有涉及这个的人.任何限制?解决方法我建议从AlexWollmer开始使用thisblogpost.他创建了一个非常有用的JavaScript库:tuneup_jswithtest()函数,它允许测试分离和有用的帮助者以及为自动化仪器编写测试的断言.

随机推荐

  1. js中‘!.’是什么意思

  2. Vue如何指定不编译的文件夹和favicon.ico

    这篇文章主要介绍了Vue如何指定不编译的文件夹和favicon.ico,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  3. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  4. jquery点赞功能实现代码 点个赞吧!

    点赞功能很多地方都会出现,如何实现爱心点赞功能,这篇文章主要为大家详细介绍了jquery点赞功能实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  5. AngularJs上传前预览图片的实例代码

    使用AngularJs进行开发,在项目中,经常会遇到上传图片后,需在一旁预览图片内容,怎么实现这样的功能呢?今天小编给大家分享AugularJs上传前预览图片的实现代码,需要的朋友参考下吧

  6. JavaScript面向对象编程入门教程

    这篇文章主要介绍了JavaScript面向对象编程的相关概念,例如类、对象、属性、方法等面向对象的术语,并以实例讲解各种术语的使用,非常好的一篇面向对象入门教程,其它语言也可以参考哦

  7. jQuery中的通配符选择器使用总结

    通配符在控制input标签时相当好用,这里简单进行了jQuery中的通配符选择器使用总结,需要的朋友可以参考下

  8. javascript 动态调整图片尺寸实现代码

    在自己的网站上更新文章时一个比较常见的问题是:文章插图太宽,使整个网页都变形了。如果对每个插图都先进行缩放再插入的话,太麻烦了。

  9. jquery ajaxfileupload异步上传插件

    这篇文章主要为大家详细介绍了jquery ajaxfileupload异步上传插件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. React学习之受控组件与数据共享实例分析

    这篇文章主要介绍了React学习之受控组件与数据共享,结合实例形式分析了React受控组件与组件间数据共享相关原理与使用技巧,需要的朋友可以参考下

返回
顶部