澳门新萄京官方网站-www.8455.com-澳门新萄京赌场网址

澳门新萄京官方网站深入之bind的模拟实现,bin

2019-06-16 作者:澳门新萄京赌场网址   |   浏览(176)

关于 bind 你可能需要了解的知识点以及使用场景

2016/08/18 · JavaScript · bind

本文作者: 伯乐在线 - 韩子迟 。未经作者许可,禁止转载!
欢迎加入伯乐在线 专栏作者。

不看不知道,一看吓一跳,已经整整一个月没有更新 underscore 源码解读系列文章了。前面我们已经完成了 Object ,Array,Collection 上的扩展方法的源码剖析,本文开始来解读 Function 上的扩展方法。

完整的 underscore 源码解读系列文章请移步 ,觉得还阔以的话,给个 star 鼓励下楼主呗 ^_^

这篇文章主要介绍了Javascript Function.prototype.bind详细分析的相关资料,需要的朋友可以参考下

JavaScript 深入之bind的模拟实现

2017/05/26 · JavaScript · bind

原文出处: 冴羽   

javascript中bind函数的作用实例介绍,javascriptbind

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
button {background-color:#0f0;}
</style>
</head>
<body>
<button id="button"> 按钮 </button>
<input type="text">
<script>
var button = document.getElementById("button");
button.onclick = function() {
alert(this.id); // 弹出button
};
//可以看出上下文的this 为button
</script>
</body>
</html>

此时加入bind

复制代码 代码如下:

 var text = document.getElementById("text");
 var button = document.getElementById("button");
 button.onclick = function() {
 alert(this.id); // 弹出button
 }.bind(text);
 //可以看出上下文的this 为button

此时会发现this改变为text

函数字面量里也适用,目的是保持上下指向(this)不变。

var obj = {
color: "#ccc", 
element: document.getElementById('text'),
events: function() {
document.getElementById("button").addEventListener("click", function(e) {
console.log(this);
this.element.style.color = this.color;
}.bind(this))
return this;
},
init: function() {
this.events();
}
};
obj.init();

此时点击按钮text里的字会变色。可见this不为button而是obj。

bind()的方法在ie,6,7,8中不适用,需要扩展通过扩展Function prototype可以实现此方法。

if (!Function.prototype.bind) {

Function.prototype.bind = function(obj) {
var slice = [].slice, args = slice.call(arguments, 1), self = this, nop = function() {
}, bound = function() {
return self.apply(this instanceof nop ? this : (obj || {}),
args.concat(slice.call(arguments)));
};

nop.prototype = self.prototype;

bound.prototype = new nop();

return bound;
};
}

此时可以看到ie6,7,8中也支持bind()。

复制代码 代码如下:

slice = Array.prototype.slice,

array = Array.prototype.slice.call( array, 0 );

将类似数组转换为数组

 

bind 简介

Ok,今天要讲的正是 bind。关于 bind,可以先移步楼主以前写的文章 ECMAScript 5(ES5) 中 bind 方法简介备忘,有个基本的概念。

bind() 方法会创建一个新函数,当这个新函数被调用时,它的 this 值是传递给 bind() 的第一个参数, 它的参数是 bind() 的其他参数和其原本的参数。

语法是这样样子的:

fun.bind(thisArg[, arg1[, arg2[, ...]]])

1
fun.bind(thisArg[, arg1[, arg2[, ...]]])
  • thisArg 当绑定函数被调用时,该参数会作为原函数运行时的 this 指向。当使用 new 操作符调用绑定函数时,该参数无效。
  • arg1, arg2, … (可选)当绑定函数被调用时,这些参数加上绑定函数本身的参数会按照顺序作为原函数运行时的参数。

  Function.prototype.bind分析

bind

一句话介绍 bind:

bind() 方法会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数。(来自于 MDN )

由此我们可以首先得出 bind 函数的两个特点:

  1. 返回一个函数
  2. 可以传入参数

javascript bind的用法

javascript的bind

bind主要是为了改变函数内部的this指向,这个是在ECMA5以后加入的,所以IE8一下的浏览器不支持

bind方法会创建一个新函数,称为绑定函数.当调用这个绑定函数时,绑定函数会以创建它时传入bind方法的第一个参数作为this,传入bind方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数.

可以看这段实例:
var logger = { x: 0, updateCount: function(){ this.x ; console.log(this.x); }} // 下面两段代码的实现是一样的 document.querySelector('button').addEventListener('click', function(){ logger.updateCount();}); //用这种方式让代码更简洁,当然这只是bind的一种场景document.querySelector('button').addEventListener('click', logger.updateCount.bind(logger));  

1.应用案例:

参数

bind 的第一个参数会作为原函数运行时的 this 指向,不多说;而第二个开始的参数是可选的,当绑定函数被调用时,这些参数加上绑定函数本身的参数会按照顺序作为原函数运行时的参数。怎么理解?

function fn(a, b, c) { return a b c; } var _fn = fn.bind(null, 10); var ans = _fn(20, 30); // 60

1
2
3
4
5
6
function fn(a, b, c) {
  return a b c;
}
 
var _fn = fn.bind(null, 10);
var ans = _fn(20, 30); // 60

fn 函数需要三个参数,_fn 函数将 10 作为默认的第一个参数,所以只需要传入两个参数即可,如果你不小心传入了三个参数,放心,也只会取前两个。

function fn(a, b, c) { return a b c; } var _fn = fn.bind(null, 10); var ans = _fn(20, 30, 40); // 60

1
2
3
4
5
6
function fn(a, b, c) {
  return a b c;
}
 
var _fn = fn.bind(null, 10);
var ans = _fn(20, 30, 40); // 60

这有啥用呢?如果某些函数,前几个参数已经 “内定” 了,我们便可以用 bind 返回一个新的函数。也就是说,bind() 能使一个函数拥有预设的初始参数。这些参数(如果有的话)作为 bind() 的第二个参数跟在 this 后面,之后它们会被插入到目标函数的参数列表的开始位置,传递给绑定函数的参数会跟在它们的后面。

function list() { return Array.prototype.slice.call(arguments); } var list1 = list(1, 2, 3); // [1, 2, 3] // Create a function with a preset leading argument var leadingThirtysevenList = list.bind(undefined, 37); var list2 = leadingThirtysevenList(); // [37] var list3 = leadingThirtysevenList(1, 2, 3); // [37, 1, 2, 3]

1
2
3
4
5
6
7
8
9
10
11
function list() {
  return Array.prototype.slice.call(arguments);
}
 
var list1 = list(1, 2, 3); // [1, 2, 3]
 
// Create a function with a preset leading argument
var leadingThirtysevenList = list.bind(undefined, 37);
 
var list2 = leadingThirtysevenList(); // [37]
var list3 = leadingThirtysevenList(1, 2, 3); // [37, 1, 2, 3]

bind()方法会创建一个新的函数,成为绑定函数。当调用这个绑定函数时,绑定函数会以创建它时传入的第一个参数作为this,传入bind()方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调取原函数。

返回函数的模拟实现

从第一个特点开始,我们举个例子:

var foo = { value: 1 }; function bar() { console.log(this.value); } // 返回了一个函数 var bindFoo = bar.bind(foo); bindFoo(); // 1

1
2
3
4
5
6
7
8
9
10
11
12
var foo = {
    value: 1
};
 
function bar() {
    console.log(this.value);
}
 
// 返回了一个函数
var bindFoo = bar.bind(foo);
 
bindFoo(); // 1

关于指定 this 的指向,我们可以使用 call 或者 apply 实现,关于 call 和 apply 的模拟实现,可以查看《JavaScript深入之call和apply的模拟实现》。我们来写第一版的代码:

// 第一版 Function.prototype.bind2 = function (context) { var self = this; return function () { self.apply(context); } }

1
2
3
4
5
6
7
8
// 第一版
Function.prototype.bind2 = function (context) {
    var self = this;
    return function () {
        self.apply(context);
    }
 
}

javascript:对于Functionprototypebind()这个方法

Function.prototype.bind=function(thisArg){ var fn=this, slice=Array.prototype.slice, args=slice.call(arguments,1);//arguments1 var a1 = arguments; return function(){ alert(a1 == arguments);// 判断是否为同一个 return fn.apply(thisArg,args.concat(slice.call(arguments)));//arguments2 }};((function(){}).bind())(2);// 总是alert出false不是。第一个arguments是只thisArg,第二个则是指返回的那个函数的arguments。

Function.prototype.bind=function(thisArg){ var fn=this, slice=Array.prototype.slice, args=slice.call(arguments,1);//arguments1 alert(arguments[0]);// alert出1 return function(){ alert(arguments[0]);// alert出2 return fn.apply(thisArg,args.concat(slice.call(arguments)));//arguments2 }};((function(){}).bind(1))(2);  

!DOCTYPE htmlhtmlheadmeta charset="utf-8"stylebutton {background-color:#0f0;}/style/headbodybutton id="button" 按钮...

  

new

使用 bind 返回的结果还是个 function,是个 function 就可以被 new 运算符调用,那么结果呢?规范中说的很清楚了,当使用 new 操作符调用绑定函数时,bind 的第一个参数无效。

function Person(name, age) { this.name = name; this.age = age; } var _Person = Person.bind({}); var p = new _Person('hanzichi', 30); // Person {name: "hanzichi", age: 30}

1
2
3
4
5
6
7
function Person(name, age) {
  this.name = name;
  this.age = age;
}
 
var _Person = Person.bind({});
var p = new _Person('hanzichi', 30); // Person {name: "hanzichi", age: 30}

一般我们不会去这么用,但是如果要写个 bind 的 polyfill(),还是需要考虑用 new 调用的情况。

我们也可以设置默认值(参考上一小节),原先提供的那些参数仍然会被前置到构造函数调用的前面。

function Person(name, age) { this.name = name; this.age = age; } var _Person = Person.bind(null, 'hanzichi'); var p = new _Person(30); // Person {name: "hanzichi", age: 30}

1
2
3
4
5
6
7
function Person(name, age) {
  this.name = name;
  this.age = age;
}
 
var _Person = Person.bind(null, 'hanzichi');
var p = new _Person(30); // Person {name: "hanzichi", age: 30}

      实际使用中我们经常会碰到这样的问题:

传参的模拟实现

接下来看第二点,可以传入参数。这个就有点让人费解了,我在 bind 的时候,是否可以传参呢?我在执行 bind 返回的函数的时候,可不可以传参呢?让我们看个例子:

var foo = { value: 1 }; function bar(name, age) { console.log(this.value); console.log(name); console.log(age); } var bindFoo = bar.bind(foo, 'daisy'); bindFoo('18'); // 1 // daisy // 18

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var foo = {
    value: 1
};
 
function bar(name, age) {
    console.log(this.value);
    console.log(name);
    console.log(age);
 
}
 
var bindFoo = bar.bind(foo, 'daisy');
bindFoo('18');
// 1
// daisy
// 18

函数需要传 name 和 age 两个参数,竟然还可以在 bind 的时候,只传一个 name,在执行返回的函数的时候,再传另一个参数 age!

这可咋办?不急,我们用 arguments 进行处理:

// 第二版 Function.prototype.bind2 = function (context) { var self = this; // 获取bind2函数从第二个参数到最后一个参数 var args = Array.prototype.slice.call(arguments, 1); return function () { // 这个时候的arguments是指bind返回的函数传入的参数 var bindArgs = Array.prototype.slice.call(arguments); self.apply(context, args.concat(bindArgs)); } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 第二版
Function.prototype.bind2 = function (context) {
 
    var self = this;
    // 获取bind2函数从第二个参数到最后一个参数
    var args = Array.prototype.slice.call(arguments, 1);
 
    return function () {
        // 这个时候的arguments是指bind返回的函数传入的参数
        var bindArgs = Array.prototype.slice.call(arguments);
        self.apply(context, args.concat(bindArgs));
    }
 
}

        var Mouse = function () {

配合 setTimeout

什么时候容易丢失 this 指向?恩,setTimeout 是一个场景,很容易把 this 指向 window,当然,setInterval 也是一样。当使用对象的方法时,需要 this 引用对象,你可能需要显式地把 this 绑定到回调函数以便继续使用对象。

var canvas = { render: function() { this.update(); this.draw(); }, update: function() { // ... }, draw: function() { // ... } }; window.setInterval(canvas.render, 1000 / 60);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var canvas = {
  render: function() {
    this.update();
    this.draw();
  },
 
  update: function() {
    // ...
  },
 
  draw: function() {
    // ...
  }
};
 
window.setInterval(canvas.render, 1000 / 60);

用 canvas 写特效或者做游戏时经常会碰到类似的问题。上面的代码是有问题的,render 方法中的 this 其实被指向了 window!我们可以用 bind,显式地把 this 绑定到回调函数以便继续使用该对象。

window.setInterval(canvas.render.bind(canvas), 1000);

1
window.setInterval(canvas.render.bind(canvas), 1000);

类似的情况还有 dom 的事件监听,一不小心可能 this 就被指向了 dom 元素。可以参考下以前做 bigrender 时写的这部分代码 。

var name = "pig";

构造函数效果的模拟实现

完成了这两点,最难的部分到啦!因为 bind 还有一个特点,就是

一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。

也就是说当 bind 返回的函数作为构造函数的时候,bind 时指定的 this 值会失效,但传入的参数依然生效。举个例子:

var value = 2; var foo = { value: 1 }; function bar(name, age) { this.habit = 'shopping'; console.log(this.value); console.log(name); console.log(age); } bar.prototype.friend = 'kevin'; var bindFoo = bar.bind(foo, 'daisy'); var obj = new bindFoo('18'); // undefined // daisy // 18 console.log(obj.habit); console.log(obj.friend); // shopping // kevin

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var value = 2;
 
var foo = {
    value: 1
};
 
function bar(name, age) {
    this.habit = 'shopping';
    console.log(this.value);
    console.log(name);
    console.log(age);
}
 
bar.prototype.friend = 'kevin';
 
var bindFoo = bar.bind(foo, 'daisy');
 
var obj = new bindFoo('18');
// undefined
// daisy
// 18
console.log(obj.habit);
console.log(obj.friend);
// shopping
// kevin

注意:尽管在全局和 foo 中都声明了 value 值,最后依然返回了 undefind,说明绑定的 this 失效了,如果大家了解 new 的模拟实现,就会知道这个时候的 this 已经指向了 obj。

(哈哈,我这是为我的下一篇文章《JavaScript深入系列之new的模拟实现》打广告)。

所以我们可以通过修改返回的函数的原型来实现,让我们写一下:

// 第三版 Function.prototype.bind2 = function (context) { var self = this; var args = Array.prototype.slice.call(arguments, 1); var fbound = function () { var bindArgs = Array.prototype.slice.call(arguments); // 当作为构造函数时,this 指向实例,self 指向绑定函数,因为下面一句 `fbound.prototype = this.prototype;`,已经修改了 fbound.prototype 为 绑定函数的 prototype,此时结果为 true,当结果为 true 的时候,this 指向实例。 // 当作为普通函数时,this 指向 window,self 指向绑定函数,此时结果为 false,当结果为 false 的时候,this 指向绑定的 context。 self.apply(this instanceof self ? this : context, args.concat(bindArgs)); } // 修改返回函数的 prototype 为绑定函数的 prototype,实例就可以继承函数的原型中的值 fbound.prototype = this.prototype; return fbound; }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 第三版
Function.prototype.bind2 = function (context) {
    var self = this;
    var args = Array.prototype.slice.call(arguments, 1);
 
    var fbound = function () {
 
        var bindArgs = Array.prototype.slice.call(arguments);
        // 当作为构造函数时,this 指向实例,self 指向绑定函数,因为下面一句 `fbound.prototype = this.prototype;`,已经修改了 fbound.prototype 为 绑定函数的 prototype,此时结果为 true,当结果为 true 的时候,this 指向实例。
        // 当作为普通函数时,this 指向 window,self 指向绑定函数,此时结果为 false,当结果为 false 的时候,this 指向绑定的 context。
        self.apply(this instanceof self ? this : context, args.concat(bindArgs));
    }
    // 修改返回函数的 prototype 为绑定函数的 prototype,实例就可以继承函数的原型中的值
    fbound.prototype = this.prototype;
    return fbound;
}

如果对原型链稍有困惑,可以查看《JavaScript深入之从原型到原型链》。

            // Look! no that = this!

tip

bind 还能做一些有意思的事情。

通常来说,将一个类数组转为数组,我们会用 slice(ie9- 不支持)。参考

var slice = Array.prototype.slice; // slice.apply(arguments); // slice(arguments, 1);

1
2
3
4
var slice = Array.prototype.slice;
 
// slice.apply(arguments);
// slice(arguments, 1);

bind 能让调用变的更加简单。

// same as "slice" in the previous example var unboundSlice = Array.prototype.slice; var slice = Function.prototype.call.bind(unboundSlice); // ... slice(arguments); // slice(arguments, 1);

1
2
3
4
5
6
7
8
// same as "slice" in the previous example
var unboundSlice = Array.prototype.slice;
var slice = Function.prototype.call.bind(unboundSlice);
 
// ...
 
slice(arguments);
// slice(arguments, 1);

再举个类似的例子,比如说我们要添加事件到多个节点,for 循环当然没有任何问题,我们还可以 “剽窃” forEach 方法:

Array.prototype.forEach.call(document.querySelectorAll('input[type="button"]'), function(el){ el.addEventListener('click', fn); });

1
2
3
Array.prototype.forEach.call(document.querySelectorAll('input[type="button"]'), function(el){
  el.addEventListener('click', fn);
});

更进一步,我们可以用 bind 将函数封装的更好:

var unboundForEach = Array.prototype.forEach , forEach = Function.prototype.call.bind(unboundForEach); forEach(document.querySelectorAll('input[type="button"]'), function (el) { el.addEventListener('click', fn); });

1
2
3
4
5
6
var unboundForEach = Array.prototype.forEach
  , forEach = Function.prototype.call.bind(unboundForEach);
 
forEach(document.querySelectorAll('input[type="button"]'), function (el) {
  el.addEventListener('click', fn);
});

同样类似的,我们可以将 x.y(z) 变成 y(x,z) 的形式:

var obj = { num: 10, getCount: function() { return this.num; } }; var unboundBind = Function.prototype.bind , bind = Function.prototype.call.bind(unboundBind); var getCount = bind(obj.getCount, obj); console.log(getCount()); // 10

1
2
3
4
5
6
7
8
9
10
11
12
var obj = {
  num: 10,
  getCount: function() {
    return this.num;
  }
};
 
var unboundBind = Function.prototype.bind
  , bind = Function.prototype.call.bind(unboundBind);
 
var getCount = bind(obj.getCount, obj);
console.log(getCount());  // 10

再举个栗子。每隔一秒在控制台打印 1-5,看起来是道考察闭包的经典题目。

for(var i = 1; i <= 5; i ) { !function(i) { setTimeout(function() { console.log(i); }, i * 1000); }(i); }

1
2
3
4
5
6
7
for(var i = 1; i <= 5; i ) {
  !function(i) {
    setTimeout(function() {
      console.log(i);
    }, i * 1000);
  }(i);
}

ES6 下能用 let

for(let i = 1; i <= 5; i ) { setTimeout(function() { console.log(i); }, i * 1000); }

1
2
3
4
5
for(let i = 1; i <= 5; i ) {
  setTimeout(function() {
    console.log(i);
  }, i * 1000);
}

也可以用 bind,瞬间逼格提升:

for(var i = 1; i <= 5; i ) { setTimeout(console.log.bind(console, i), i * 1000); }

1
2
3
for(var i = 1; i <= 5; i ) {
  setTimeout(console.log.bind(console, i), i * 1000);
}

function Person(name){

构造函数效果的优化实现

但是在这个写法中,我们直接将 fbound.prototype = this.prototype,我们直接修改 fbound.prototype 的时候,也会直接修改函数的 prototype。这个时候,我们可以通过一个空函数来进行中转:

// 第四版 Function.prototype.bind2 = function (context) { var self = this; var args = Array.prototype.slice.call(arguments, 1); var fNOP = function () {}; var fbound = function () { var bindArgs = Array.prototype.slice.call(arguments); self.apply(this instanceof self ? this : context, args.concat(bindArgs)); } fNOP.prototype = this.prototype; fbound.prototype = new fNOP(); return fbound; }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 第四版
Function.prototype.bind2 = function (context) {
 
    var self = this;
    var args = Array.prototype.slice.call(arguments, 1);
 
    var fNOP = function () {};
 
    var fbound = function () {
        var bindArgs = Array.prototype.slice.call(arguments);
        self.apply(this instanceof self ? this : context, args.concat(bindArgs));
    }
    fNOP.prototype = this.prototype;
    fbound.prototype = new fNOP();
    return fbound;
 
}

到此为止,大的问题都已经解决,给自己一个赞!o( ̄▽ ̄)d

            this.position = [0, 0];

Read more

关于 bind 的介绍就到这里,下一篇文章将结合 underscore 来讲讲如何实现一个 bind 的 polyfill。

  • Function.prototype.bind()
  • ECMAScript 5(ES5) 中 bind 方法简介备忘
  • Javascript 中的 Bind ,Call 以及 Apply
  • Javascript 中 bind() 方法的使用与实现

打赏支持我写出更多好文章,谢谢!

打赏作者

  this.name = name;

三个小问题

接下来处理些小问题:

1.apply 这段代码跟 MDN 上的稍有不同

在 MDN 中文版讲 bind 的模拟实现时,apply 这里的代码是:

self.apply(this instanceof self ? this : context || this, args.concat(bindArgs))

1
self.apply(this instanceof self ? this : context || this, args.concat(bindArgs))

多了一个关于 context 是否存在的判断,然而这个是错误的!

举个例子:

var value = 2; var foo = { value: 1, bar: bar.bind(null) }; function bar() { console.log(this.value); } foo.bar() // 2

1
2
3
4
5
6
7
8
9
10
11
var value = 2;
var foo = {
    value: 1,
    bar: bar.bind(null)
};
 
function bar() {
    console.log(this.value);
}
 
foo.bar() // 2

以上代码正常情况下会打印 2,如果换成了 context || this,这段代码就会打印 1!

所以这里不应该进行 context 的判断,大家查看 MDN 同样内容的英文版,就不存在这个判断!

2.调用 bind 的不是函数咋办?

不行,我们要报错!

if (typeof this !== "function") { throw new Error("Function.prototype.bind - what is trying to be bound is not callable"); }

1
2
3
if (typeof this !== "function") {
  throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
}

3.我要在线上用

那别忘了做个兼容:

Function.prototype.bind = Function.prototype.bind || function () { …… };

1
2
3
Function.prototype.bind = Function.prototype.bind || function () {
    ……
};

当然最好是用es5-shim啦。

            if (document.addEventListener) {

打赏支持我写出更多好文章,谢谢!

澳门新萄京官方网站 1

1 赞 7 收藏 评论

  this.getName = function(){

最终代码

所以最最后的代码就是:

Function.prototype.bind2 = function (context) { if (typeof this !== "function") { throw new Error("Function.prototype.bind - what is trying to be bound is not callable"); } var self = this; var args = Array.prototype.slice.call(arguments, 1); var fNOP = function () {}; var fbound = function () { self.apply(this instanceof self ? this : context, args.concat(Array.prototype.slice.call(arguments))); } fNOP.prototype = this.prototype; fbound.prototype = new fNOP(); return fbound; }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Function.prototype.bind2 = function (context) {
 
    if (typeof this !== "function") {
      throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
    }
 
    var self = this;
    var args = Array.prototype.slice.call(arguments, 1);
    var fNOP = function () {};
 
    var fbound = function () {
        self.apply(this instanceof self ? this : context, args.concat(Array.prototype.slice.call(arguments)));
    }
 
    fNOP.prototype = this.prototype;
    fbound.prototype = new fNOP();
 
    return fbound;
 
}

                document.addEventListener('mousemove', ?);   //this.move?  

关于作者:韩子迟

澳门新萄京官方网站 2

a JavaScript beginner 个人主页 · 我的文章 · 9 ·    

澳门新萄京官方网站 3

    setTimeout(function(){

深入系列

JavaScript深入系列目录地址:。

JavaScript深入系列预计写十五篇左右,旨在帮大家捋顺JavaScript底层知识,重点讲解如原型、作用域、执行上下文、变量对象、this、闭包、按值传递、call、apply、bind、new、继承等难点概念。

如果有错误或者不严谨的地方,请务必给予指正,十分感谢。如果喜欢或者有所启发,欢迎star,对作者也是一种鼓励。

本系列:

  1. JavaScirpt 深入之从原型到原型链
  2. JavaScript 深入之词法作用域和动态作用域
  3. JavaScript 深入之执行上下文栈
  4. JavaScript 深入之变量对象
  5. JavaScript 深入之作用域链
  6. JavaScript 深入之从 ECMAScript 规范解读 this
  7. JavaScript 深入之执行上下文
  8. JavaScript 深入之闭包
  9. JavaScript 深入之参数按值传递
  10. JavaScript 深入之call和apply的模拟实现

    1 赞 收藏 评论

澳门新萄京官方网站 4

            } else if (document.attachEvent) {

      console.log("Hello,my name is " this.name);

                document.attachEvent("onmousemove", ?);     //this.move?怎么放进去

    },100);

            }

  }

 

}

        };

var weiqi = new Person("卫旗");

        Mouse.prototype.move = function (arg1,arg2,event) {

weiqi.getName(); 

            event = window.event || event;

//Hello,my name is pig

            var x = event.pageX || event.offsetX,

      这个时候输出this.name是pig,原因是this的指向是在运行函数时确定的,而不是在定义函数时确定的,再因为setTimeout是在全局环境下只想,所以this就指向了window。

        y = event.pageY || event.offsetY;

      以前解决这个问题的办法通常是缓存this,例如:

            this.position = position = [x, y];

var name = "pig";

            this.log(arg1,arg2);

function Person(name){

        };

  this.name = name;

        Mouse.prototype.log = function (arg1, arg2) {

  this.getName = function(){

            console.log(arg1 "," arg2);

    //在这里缓存一个this

            console.log(this.position);

    var self = this;

        };

    setTimeout(function(){

        new Mouse();

      //在这里是有缓存this的self

www.2cto.com

      console.log("Hello,my name is " self.name);

上面你知道'?'号那里要干嘛了吗?我想给document的mousemove绑定我的move方法,但是遇到难题了,这样的话,Mouse.prototype.move

    },100);

里的this就不会指向Mouse的对象,相信大家经常碰到这种问题.也许你早知道了怎么解决,但是有更快更简单的方法吗?答案是:

  }

  Function.prototype.bind()这个神奇的玩意,但是ie6 7 8都不支持,一般现代浏览器都支持了,我们接下来要做的就是模仿他,

}

 这么好的方法当然要模仿它,怎么模仿见下面nothing的原创方法

var weiqi = new Person("卫旗");

    (function () {

weiqi.getName();

            var proxy = function (fn, target) {

//Hello,my name is 卫旗

                var proxy = function () {

      这样就解决了这个问题,非常方便,因为它使得setTimeout函数中可以访问Person的上下文。

                    if (2 < arguments.length) { //存在被代理的函数有参数的时候

      现在有一个更好的解决办法,可以使用bind()函数,上面的例子可以被更新为:

                        var privateArgs = Array.prototype.slice.call(arguments, 2);

var name = "pig";

                      //从第二个开始取出来,[this,绑定的对象,参数列表]

function Person(name){

                        return function () {

  this.name = name;

                            var args = Array.prototype.slice.call(arguments);

  this.getName = function(){

        -->这里的arguments与外面的不是同一个,这个是被代理的函数内部的arguments对象,

    setTimeout(function(){

       比如这里的move函数的  arguments[0]=[object Event]就是这个事件内部的e参数

      console.log("Hello,my name is " this.name);

 

    }.bind(this),100);

                            Array.prototype.unshift.apply(args, privateArgs);

    //注意上面这一行,添加了bind(this)

 

  }

       -->这里在加上传进来的参数,就实现了,和原生bind一样的参数形式

}

     //->而且这里是把私有的参数放到前面的比如a=new Mouse();a.move(1,2);

var weiqi = new Person("卫旗");

      //如果这个move方法没有参数,意思就是prototype.move=fn(){arguments} ,

weiqi.getName();

      //而我传进来了参数,参数的arguments.length=3,

//Hello,my name is 卫旗

       //arguments[0]=1,arguments[1]=2,arguments[2]=[object event].

      bind()最简单的用法是创建一个函数,使得这个函数无论怎么样调用都拥有同样的this值。JavaScript新手经常犯的一个错误就是将一个方法从一个对象中拿出来,然后再调用,希望方法中的this是原来的对象(比如在回调函数中传入这个方法)。如果不做特殊处理的话,一般会丢失原来的对象。从原来的函数和原来的对象创建一个绑定函数,则可以很漂亮的解决这个问题:

 

//定义全局变量x

                            return fn.apply(target, args);

var x = "window";

                        }

澳门新萄京官方网站深入之bind的模拟实现,bind详细分析。//在module内部定义x

                //这里之所以搞复杂了,是因为,在被代理的函数可以直接访问arguments,比如我不给被代理的函数传参数,而直接使用

var module = {

                 //这样这个arguments就会包含与原生Function.prototype.bind的arguments一样的对象,

  x:"module",

                 //这里代码深奥,是因为你没理解这里原生的bind里面的arguments是什么,知道了,就知道为什么绑定我自己的arguments

  getX:function(){

                //做这么多,主要目的就是使你被代理的函数内部的arguments与function.prototype.bind里的arguments对象包含的东西一致

    console.log(this.x);

                    }

  }

             

}

                    return function () {

module.getX();

                        return fn.apply(target, arguments);

//返回module,因为在module内部调用getX()

                    }

var getX = module.getX;

                }

getX();

                return proxy.apply(null, arguments);

//返回window,因为这个getX()是在全局作用域中调用的

            };

//绑定getX()并将this值设为module

            /*支持原生的使用原生的*/

var boundGetX = getX.bind(module);

            Function.prototype.bind = Function.prototype.bind ||

boundGetX();

    function (target) {                   //这里的this指代要被代理的函数

//返回module,绑定以后this值始终为module

        if (1 < arguments.length) {

浏览器支持情况:

            var args = Array.prototype.slice.call(arguments, 1);  //取出参数列表

Browser Version support

            args.unshift(this, target);  //这个args最终变成了[this,绑定的对象,参数列表]

Chrome 7

            return proxy.apply(null, args);

FireFox(Gecko) 4.0(2)

 

Internet Explorer 9

    -->估计大家会跟17楼犯一样的错误,这里之所以这么复杂的操作arguments对象,只是为了能保证传进proxy函数中,保证arguments对象不失效

Opera 11.60

        }

Safari 5.14

        return proxy(this, target);

      很不幸,Function.prototype.bind在IE8及以下版本中不被支持,所以如果没有一个备选方案的话,可能会在运行时出现问题。bind函数在ECMA-262第五版才被加入。它可能不无法在所有浏览器上运行。你可以在脚本部分加入如下代码,让不支持的浏览器也能使用bind()功能。

    };

if (!Function.prototype.bind) {

        })();

Function.prototype.bind = function (oThis) {

www.2cto.com

  if (typeof this !== "function") {

以上代码为什么我要一直return回来代理,因为这样你才能这样调用this.move.bind(this,1,2)()然后这里会立即执行函数!

  // 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");

 if (document.addEventListener) {

  }

                document.addEventListener('mousemove', this.move.bind(this,1,2));

  var aArgs = Array.prototype.slice.call(arguments, 1),

            } else if (document.attachEvent) {

    fToBind = this,

                document.attachEvent("onmousemove", this.move.bind(this,1,2));

    fNOP = function () {},

            }

    fBound = function () {

www.2cto.com

    return fToBind.apply(this instanceof fNOP && oThis

 是不是以后凡是碰到要添加事件,然后调用的方法的this又想指向其他对象,这样是不是很简单呢..

                ? this

看到大家对以上代码有点难理解,来个简单点得

                : oThis || window,

 var a = function () {

                aArgs.concat(Array.prototype.slice.call(arguments)));

            console.log(arguments[0]);   //1

    };

            console.log(arguments[1]);   //2

  fNOP.prototype = this.prototype;

            console.log(this.key1);

  fBound.prototype = new fNOP();

            //这样绑定参数的话,我的参数列出来才能和原生的bind一样,就这么简单,

  return fBound;

        };

};

        var b = {

}

            key1: "value1"

语法

        };

    fun.bind(thisArg[, arg1[, arg2[, …]]])

 

参数

     a.bind(b, 1, 2)();

      thisArg,当绑定函数被调用时,该参数会作为原函数运行时的this指向,当使用new操作符调用绑定函数时,该参数无效。

www.2cto.com

      arg1, arg2, …,当绑定函数被调用时,这些参数加上绑定函数本身的参数会按照顺序作为原函数运行时的参数。

 

描述

反驳17楼同学的代码错误,我想这是很多人会犯的错误,代码如下

      bind()函数会创建一个新的函数(一个绑定的函数)有同样的函数体(在ECMAScript 5 规范内置Call属性),当该函数(绑定函数的原函数)被调用时this值绑定到bind()的第一个参数,该参数不能被重写。绑定函数被调用时,bind()也接受预设的参数提供给原函数。一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的this值被忽略,同事调用的参数被提供给模拟函数。

      Function.prototype.bind = function (target) {

            var self = this;

            return function () {

                return self.apply(target, arguments); //这里的arguments根本传不进来

            }

        }

          var a = function () {

              console.log(arguments.length);  //这样bind的话,arguments参数失效

                                             //arguments.length=0.

              console.log(this.key1);

          };

        var b = {

                    key1: "value1"

          };

                a.bind(b, [1, 2], 3)();    //从这里可以看出,期望的arguments.length=2

                //这也是我为什么苦口婆心的操作arguments参数

        //我知道这里大部分人都会觉得对的,但是你错了,17楼的同学你还得在思考下

 

不带注释的源码,

     (function () {

            var proxy = function (fn, target) {

                var proxy = function () {

                    if (2 < arguments.length) {

                        var privateArgs = Array.prototype.slice.call(arguments, 2);

                        return function () {

                            var args = Array.prototype.slice.call(arguments);

                            Array.prototype.unshift.apply(args,privateArgs);

                            return fn.apply(target, args);

                        }

                    }

                    return function () {

                        return fn.apply(target, arguments);

                    }

                }

                return proxy.apply(null, arguments);

            };

            /*支持原生的使用原生的*/

            Function.prototype.bind = Function.prototype.bind ||

    function (target) {               

        if (1 < arguments.length) {

            var args = Array.prototype.slice.call(arguments, 1);

            args.unshift(this, target); 

            return proxy.apply(null, args);

        }

        return proxy(this, target);

    };

        })();

www.2cto.com

 

 

 

 

如若需要转载本文,请附上链接

 

 

  

: var Mouse = function () { // Look! no that = this! this.position = [0, 0]; if (document.addEventListener) { document.addEventListener(mousemove, ?); //this.move? }...

本文由澳门新萄京官方网站发布于澳门新萄京赌场网址,转载请注明出处:澳门新萄京官方网站深入之bind的模拟实现,bin

关键词: