原生JS实现bind()函数

一、bind()函数的两个特性:

1、bind和curring,函数科里化

function add(a, b, c) {
    var i = a+b+c;
    console.log(i);
    return i;
}

var func = add.bind(undefined, 100);//给add()传了第一个参数a
func(1, 2);//103,继续传入b和c

var func2 = func.bind(undefined, 200);//给func2传入第一个参数,也就是b,此前func已有参数a=100
func2(10);//310,继续传入c,100+200+10

  可以利用此种特性方便代码重用,如下,可以不同的页面中只需要配置某几项,前面几项固定的配置可以选择用bind函数先绑定好,讲一个复杂的函数拆分成简单的子函数。

2、bind和new

function foo() {
    this.b = 100;
    console.log(this.a);
    return this.a;
}

var func =  foo.bind({a:1});
func();//1
new func();//undefined   {b:100},可以看到此时上面的bind并不起作用

  函数中的return除非返回的是个对象,否则通过new返回的是个this,指向一个空对象,空对象原型指向foo.prototype,空对象的b属性是100。也就是说通过new的方式创建一个对象,bind()函数在this层面上并不起作用,但是需要注意在参数层面上仍起作用,如下:

function foo(c) {
    this.b = 100;
    console.log(this.a);
    console.log(c);
    return this.a;
}

var func =  foo.bind({a:1},20);
new func();//undefined 20,通过new创建对象func,bind绑定的c依旧起作用

二、bind实现

  了解完以上两个特性,再来看看bind()的实现:

if (!Function.prototype.bind) {
  Function.prototype.bind = function (oThis) {
    if (typeof this !== "function") {
      // closest thing possible to the ECMAScript 5 internal IsCallable function
      throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
    }

    var aArgs = Array.prototype.slice.call(arguments, 1),
        fToBind = this,
        fNOP = function () {},
        fBound = function () {
          return fToBind.apply(this instanceof fNOP && oThis
                                 ? this
                                 : oThis || window,
                               aArgs.concat(Array.prototype.slice.call(arguments)));
        };

    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();

    return fBound;
  };
}

  第3行:传入oThis就是foo.bind({a:1})中传入的对象{a:1};

  第4行:判断调用此bind方法的对象是不是一个函数function,若不是则报错;

  第10行:

  (1)因为函数自带的arguments属性并不是一个数组,只是一个类数组,不具有slice这些方法,所以用call方法给slice()指定this为arguments,让arguments也可以实现slice()方法。

  (2)后面传入参数1,是slice(start, end)中的一个参数start,表示从arguments的小标为1,即第二个参数开始切割。 这里是将bind函数的参数数组取出来,第一个参数不要(就是不要oThis)也就是要被绑定方法的那个对象

  (3)arguments参数只有在函数调用执行的时候才存在,也就是当var func =
foo.bind({a:1});的时候,调用了bind,此时aArgs是一个空数组。如果是var func = foo.bind({a:1},
2),那么aArgs = [2];

  第12行,13行,20行,21行:创建了一个空对象FNOP,并将这个空对象的原型指向foo的原型;

  然后又将func/fBound的原型指向一个新的FNOP实例,这个步骤完成了给func/fBound拷贝一个FNOP的prototype即this/foo的prototype。

  其实这几句就相当于fBound.prototype = Object.create(this.prototype);

  第14行:

  (1)给 fBound/func return一个fToBind/foo对象;

  (2)这里的this指的是调用func()时的执行环境;直接调用func()的时候,this指向的是全局对象,那么结果是oThis/{a:1},这样就可以让这个fToBind的this指向这个传进来的对象oThis;

  (3)bind()同时也会传参数:aArgs.concat(Array.prototype.slice.call(arguments))

  【注意】这里的arguments是调用此函数时的arguments,也就是func()的执行环境,和上面的arguments(bind的执行环境)不一样,在此例中,此时的arguments是空数组,因为并没有给func()传参数。这段contact的意思就是把bind()中传的参数和func()中传的参数连起来,来实现上面提到的bind的科里性。

  (4)如果通过new func()来调用,this会指向一个空对象,这个空对象的原型会指向构造器的prototype的属性,也就是func/fBound的prototype属性。此时this instanceof fNOP 为true,那么返回的是this就是当前正常的this;相当于忽略掉bind的this的影响,实现了上述的bind特性二:bind和new。

  那么想想为什么要给func/fBound拷贝一个FNOP的prototype即this/foo的prototype?没有实现这个会怎样?

  我们知道bind()函数其实是实现了this的指定和参数的传递; 实际中的new
func()其实相当于创建了func()一个新实例,使用的构造函数是func,它只为新对象定义了【默认的】属性和方法。也就是Object.create(this.prototype)的作用,如果不把foo的prototype拷贝个func,那么这里的new
func()就没法得到foo默认的属性。 如图我把那两行注释掉后,za没办法获取到this.b的值

原文地址:https://www.cnblogs.com/goloving/p/9380076.html

时间: 2024-08-03 16:48:27

原生JS实现bind()函数的相关文章

关于原生js中bind函数的实现

今天继续研究了bind函数的实现,也知道了shim和polyfill的说法,现在总结一下, 1 if (!Function.prototype.bind) { 2 Function.prototype.bind = function (oThis) { 3 if (typeof this !== "function") { 4 // closest thing possible to the ECMAScript 5 internal IsCallable function 5 thr

原生JS 封装运动函数

在学原生JS之前,一直用jQuery 写运动,各种方便,但是不知其所以然,今天得空看了一个javascript 视频教程(这里不说了,以防广告的嫌疑),只能用一个词语形容之后的感觉-------醍醐灌顶. /* - obj 指的是DOM对象 - json 指的是 CSS样式 - 例 startMove(oDiv,{width:100,height:100},function(){}) */ function startMove(obj,json,fnEnd){ clearInterval(obj

原生JS封装_new函数,实现new关键字的功能

一.前言 众所周知:没有对象怎么办?那就new一个! 那么在JS中,当我们new一个对象的时候,这个new关键字内部都干了什么呢?现在我们就来剖析一下原生JS中new关键字内部的工作原理. 二.原始的new 首先,我们先new一个对象看看: 1 //创建Person构造函数,参数为name,age 2 function Person(name,age){ 3 this.name = name; 4 this.age = age; 5 } 6 //实例化对象小明 7 xm = new Person

原生JS实现动画函数的封装

封装了一个JS方法,支持元素的基本动画:宽.高.透明度...等,也支持链式动画和同时运动. 获取元素的属性的函数并进行了兼容性处理: 1 function getStyle(obj, attr) { 2 if(obj.currentStyle){ //IE浏览器 3 return obj.currentStyle[attr]; 4 }else{ //chrome.firefox等浏览器 5 return getComputedStyle(obj,null)[attr]; 6 } 7 } 动画函数

原生JS封装ajax函数

1 function ajax(params) { 2 params.data = params.data || ""; 3 params.type = params.type || "get"; 4 if(!params.url) { 5 throw new Error("未指定连接"); 6 } 7 params.async = params.async || true; 8 var xhr; 9 //兼容IE 10 if(window.VB

兼容IE9以下和非IE浏览器的原生js事件绑定函数

事件绑定函数的demo如下: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta charset="UTF-8"/> <title>测试事件绑定函数</title> <scrip

【转载】JS中bind方法与函数柯里化

原生bind方法 不同于jQuery中的bind方法只是简单的绑定事件函数,原生js中bind()方法略复杂,该方法上在ES5中被引入,大概就是IE9+等现代浏览器都支持了(有关ES5各项特性的支持情况戳这里ECMAScript 5 compatibility table),权威指南上提到在ES3中利用apply模拟该方法的实现(JS权威指南中函数那章), 但无法真实还原该方法, 这也是真bind方法中的有趣特性. (原文这边理解有问题, 这段话的意思如果结合犀牛书上下文的意思, 再结合犀牛书中

原生JS实现call,apply,bind函数

1. 前言 使用原生JS实现call和apply函数,充分了解其内部原理.call和apply都是为了解决改变this的指向.作用都相同,只是传参的方式不同.除了第一个参数外,call可以接受一个参数列表,apply只接受一个参数数组. 2. call函数 2.1 描述 call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数. 2.2 语法 fun.call(thisArg, arg1, arg2, ...) 2.3 参数 thisArg:可选的.在 fun 函数

js中bind、call、apply函数的用法

最近一直在用 js 写游戏服务器,我也接触 js 时间不长,大学的时候用 js 做过一个 H3C 的 web 的项目,然后在腾讯实习的时候用 js 写过一些奇怪的程序,自己也用 js 写过几个的网站.但真正大规模的使用 js 这还是第一次.我也是初生牛犊不怕虎,这次服务器居然抛弃 C++ 和 lua 的正统搭配,而尝试用 nodejs 来写游戏服务器,折腾的自己要死要活的我也是醉了. 在给我们项目组的其他程序介绍 js 的时候,我准备了很多的内容,但看起来效果不大,果然光讲还是不行的,必须动手.