首先看call和apply,第一个参数就是改变的this指向,写谁就是谁,如果是非严格模式下,传递null或undefined指向的也是window,二者唯一的区别是执行函数时,传递的参数方式不同,call是一个个的传递,apply是把需要传递的参数放到数组中整体传递。
  ·func.call([context], x, y)
  ·func.apply([context], [x, y])
 
再看bind,它和call和apply都是改变this并且传递一些参数,不同于call和apply在改变this的同时直接把函数就执行了,bind不会立即执行函数。
 
let obj = {
fn(x, y) {
console.log(this, x, y);
}
}; obj.fn.call({}, 10, 20); // {}, 10, 20
obj.fn.apply(window, [10, 20]); //window, 10, 20 setTimeout(obj.fn.bind(30, 10, 20), 1000); //Number(30), 10, 20

先试着重写一下bind:

从参数看,首先是传递一个this指向并需要做一下处理,后续还有若干个参数

function bind(context) {
//context可能是null或undefined,需要处理一下
if (context == undefined) {
context = window;
}
//借用数组的slice方法结合arguments获取传递的指定this之后的参数集合
var args = [].slice.call(arguments, 1);
}

bind函数中的this是指最终要执行的函数,而且执行bind的时候会返回一个新的匿名函数,并且在这个新的函数中执行最终要执行的函数也就是this,并且改变其this指向:

function bind(context) {
//context可能是null或undefined,需要处理一下
if (context == undefined) {
context = window;
}
//借用数组的slice方法结合arguments获取传递的指定this之后的参数集合
var args = [].slice.call(arguments, 1);
//需要最终执行的函数
var _this = this;
return function anonymous() {
_this.apply(context, args);
};
}

这个bind函数大体算是写完,但还是有些问题,比如给元素进行事件绑定,div.onclick = obj.fn.bind(window, 10, 20),元素进行点击的时候,会有ev事件对象,相当于在执行bind函数返回的那个匿名函数中也需要传递参数,而且参数个数不确定,当然,最后还需要改写一下原型上的方法:

function bind(context) {
//context可能是null或undefined,需要处理一下
if (context == undefined) {
context = window;
}
//借用数组的slice方法结合arguments获取传递的指定this之后的参数集合
var args = [].slice.call(arguments, 1);
//需要最终执行的函数
var _this = this;
return function anonymous() {
var amArg = [].slice.call(arguments, 0);
_this.apply(context, args.concat(amArg));
};
}
Function.prototype.bind = bind;

这样,这个bind函数的重写算是完成了。

我们用es6改写一下:

function bind(context = window, ...args) {
return (...amArg) => this.call(context, ...args.concat(amArg));
}

代码看上去确实精简不少,当然也可以用apply,但经测试,性能不如call。

接下来看看重写call:

它会有若干个参数,第一个是要指向的this,直接用es6写法:

function call(context = window, ...args) {

}

函数中的this就是要调用call方法的函数,想让该函数执行并且其内部this指向传递进来的context,那么形如context.函数可以做到:

function call(context = window, ...args) {
//给context增加一个$fn属性,把当前函数赋给这个属性
context.$fn = this;
//让context.$fn这个方法执行,就是之前this函数执行,并且this指向的是context
let result = context.$fn(...args);
//增加完方法应该删除
delete context.$fn;
return result;
}
Function.prototype.call = call;

apply也就出来了:

function apply(context = window, args) {
context.$fn = this;
let result = context.$fn(...args);
delete context.$fn;
return result;
}
Function.prototype.apply = apply;

这里边还是有两个问题,一是$fn属性没有删除,目前还没想到解决办法,一个就是传进来的context必须是引用类型,但其实可以是基础类型:

function call(context = window, ...args) {
context === null ? context = window : null;
let type = typeof context;
if (type !== "object" && type !== "function" && type !== "symbol") {
//=>基本类型值
switch (type) {
case 'number':
context = new Number(context);
break;
case 'string':
context = new String(context);
break;
case 'boolean':
context = new Boolean(context);
break;
}
}
context.$fn = this;
let result = context.$fn(...args);
delete context.$fn;
return result;
}

apply的判断就不写了,但基本都实现了重写,当然,这几个方法毕竟是js的内置写法,我们只是想大致实现它们的实现原理。

 
 
 
 
 
 
 

JS-重写内置的call、apply、bind的更多相关文章

  1. js中内置有对象

    statpot:使用mongo+bootstrap+highcharts做统计报表 最近做了一个统计项目,这个统计项目大致的需求是统计接口的访问速度.客户端会调用一个接口来记录接口的访问情况,我的需求 ...

  2. js arguments 内置对象

    1.arguments是js的内置对象. 2.在不确定对象是可以用来重载函数. 3.用法如下: function goTo() { var i=arguments.length; alert(i); ...

  3. 使用原生js自定义内置标签

    使用原生js自定义内置标签 效果图 代码 <!DOCTYPE html> <html lang="en"> <head> <meta ch ...

  4. js单体内置对象

    js单体内置对象:js的内置对象,是ECMAScritp提供的.不依赖于宿主环境的对象,我的理解就是在我们开发之前js里面就已经存在的对象.单体内置对象就是是不需要通过new来实例化的,例如我们的st ...

  5. 前端面试 js 你有多了解call,apply,bind?

    函数原型链中的 apply,call 和 bind 方法是 JavaScript 中相当重要的概念,与 this 关键字密切相关,相当一部分人对它们的理解还是比较浅显,所谓js基础扎实,绕不开这些基础 ...

  6. js常用内置对象、Dom对象、BOM对象

    11.html元素事件属性中,如onclick="",双引号里可以是方法条用,可以是js代码(无需加<script>标签) 12.JavaScript内置 对象.属性和 ...

  7. JS的内置对象以及JQuery中的部分内容

     [js中的数组]              1  数组的概念:可以再内存中连续存储的多个有序元素的结构                元素的顺序:称为下标,通过下标查找对应元素.           ...

  8. js常用内置对象及方法

    在js中万物皆对象:字符串,数组,数值,函数...... 内置对象都有自己的属性和方法,访问方法如下: 对象名.属性名称: 对象名.方法名称 1.Array数组对象 unshift( )    数组开 ...

  9. js的内置对象

    转载: https://www.cnblogs.com/liuluteresa/p/6413988.html   在js里,一切皆为或者皆可以被用作对象.可通过new一个对象或者直接以字面量形式创建变 ...

  10. JS Error 内置异常类型 处理异常 Throw语句

    Exceptional Exception Handling in JavaScript       MDN资料 Anything that can go wrong, will go wrong. ...

随机推荐

  1. SqlServer分页存储过程(多表查询,多条件排序),Repeater控件呈现数据以及分页

        存储过程(Stored Procedure)是在大型数据库系统中,一组为了完成特定功能的SQL 语句集,存储在数据库中,经过第一次编译后再次调用不需要再次编译,用户通过指定存储过程的名字并给出 ...

  2. Qt Installer Framework翻译(5-2)

    创建在线安装程序 联机安装程序获取二进制安装文件中的内容以及存储库描述(Updates.xml).请创建一个存储库,并将其上传到Web服务器.然后在用于创建安装程序的config.xml文件中指定存储 ...

  3. (分块)Holes CodeForces - 13E

    题意 n(n≤105)个洞排成一条直线,第ii个洞有力量值ai,当一个球掉进洞ii时就会被立刻弹到i+ai,直到超出n.进行m(m≤105)次操作: ·修改第i个洞的力量值ai. ·在洞xx上放一个球 ...

  4. 修改 Apache 的默认端口

    修改 Apache 的默认端口修改 Apache 端口的方法是最妥协的方法了,后果是当你开启 Apache 服务器的时候,访问的本机地址将会附带端口号,比如 http://localhost:8888 ...

  5. 【中文乱码】深入分析 Java Web 中的中文编码问题

    深入分析 Java Web 中的中文编码问题 1.几种常见的编码格式 1.1 为什么要编码 在计算机中存储信息的最小单元是 1 个字节,即 8 个 bit, 所以能表示的字符范围是 0 ~ 255 个 ...

  6. ugui制作伸缩菜单

    制作一个类似与这种格式的菜单,可以伸缩滑动的.今天正好项目需要用到类似功能,所以尝试了一下,做出如下的效果 虽然只是一个思路,但是可以扩展.声明一个object物体,为but,通过GetCompone ...

  7. Python学习,第三课 - 数据类型

    前言. 本次针对Python中的数据类型,做详细的总结 1.数字 2 是一个整数的例子. 长整数 不过是大一些的整数. 3.23和52.3E-4是浮点数的例子.E标记表示10的幂.在这里,52.3E- ...

  8. zbar+opencv检测图片中的二维码或条形码

    zbar本身自带检测二维码条形码功能,这里使用opencv只是做一些简单的读取图片,灰度图片以及显示条形码和二维码时用到一些绘制 // barcode-qrcodescanner.cpp: 定义控制台 ...

  9. MySQL5.6绿色版安装

    1.下载 MySQL绿色版安装需下载好三个文件 (1).MySQL5.6版本离线安装包 (2).Microsoft Visual C++ (3).Microsoft .NET Framework 1. ...

  10. Redhat6.7 切换Centos yum源

    转自:http://inlhx.iteye.com/blog/2336729 RedHat 更换Yum源 1.检查yum包 rpm -qa |grep yum 2.删除自带包 rpm -aq | gr ...