详解call bind apply – 区别/使用场景/es6实现/es3实现

<p><code></p> <h3 id="articleHeader0">c<a href="http://www.js-code.com/tag/all" title="all" target="_blank">all</a>,apply,b<a href="http://www.js-code.com/tag/in" title="in" target="_blank">in</a>d的区别</h3> <ul> <li>apply接收<a href="http://www.js-code.com/tag/%e6%95%b0%e7%bb%84" title="数组" target="_blank">数组</a> func.apply(obj, [arus])</li> <li>c<a href="http://www.js-code.com/tag/all" title="all" target="_blank">all</a>一连串参数 func.call(obj, param1, param2....)</li> <li>b<a href="http://www.js-code.com/tag/in" title="in" target="_blank">in</a>d返回一个函数 func.bind(obj,param...)(parms...)</li> </ul> <h3 id="articleHeader1">call,apply,bind的使用场景</h3> <ul> <li> <p>将类<a href="http://www.js-code.com/tag/%e6%95%b0%e7%bb%84" title="数组" target="_blank">数组</a>/含有<a href="http://www.js-code.com/tag/length" title="length" target="_blank">length</a>属性的对象转化为数组</p> <p>类数组:(例如通过<a href="http://www.js-code.com/tag/do" title="do" target="_blank">do</a>cument.getElementsByTagName获取的元素、含有<a href="http://www.js-code.com/tag/length" title="length" target="_blank">length</a>属性的对象)具有length属性,并且可以通过0、1、2…下标来访问其中的元素,但是没有<a href="http://www.js-code.com/tag/array" title="Array" target="_blank">Array</a>中的push、pop等方法。</p> <p>注意:但是这个不适用于IE6~8,会报错,只能使用循环来解决</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="// 类数组 let trueArr = Array.prototype.slice.call(arrayLike) // 含有length属性的对象 let obj4 = { 0: 1, 1: 'thomas', 2: 13, length: 3 // 一定要有length属性 }; console.log(Array.prototype.slice.call(obj4)); // [1, &quot;thomas&quot;, 13]" title="" data-original-title="复制"></span> </div> </p></div> <pre class="javascript hljs"><code class="javascript"><span class="hljs-comment">// 类数组</span> <span class="hljs-keyword">let</span> trueArr = <span class="hljs-built_in">Array</span>.prototype.slice.call(arrayLike) <span class="hljs-comment">// 含有length属性的对象</span> <span class="hljs-keyword">let</span> obj4 = { <span class="hljs-number">0</span>: <span class="hljs-number">1</span>, <span class="hljs-number">1</span>: <span class="hljs-string">'thomas'</span>, <span class="hljs-number">2</span>: <span class="hljs-number">13</span>, <span class="hljs-attr">length</span>: <span class="hljs-number">3</span> <span class="hljs-comment">// 一定要有length属性</span> }; <span class="hljs-built_in">console</span>.log(<span class="hljs-built_in">Array</span>.prototype.slice.call(obj4)); <span class="hljs-comment">// [1, "thomas", 13]</span></code></pre> </li> <li> <p>求数组中的最大和最小值</p> <p>注意:边界问题,临界值大概在 [ 参数个数限制在65536]</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="let arr = [1,2,3,89,46] let max = Math.max.apply(null,arr)//89 let min = Math.min.apply(null,arr)//1" title="" data-original-title="复制"></span> </div> </p></div> <pre class="javascript hljs"><code class="javascript"><span class="hljs-keyword">let</span> arr = [<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">3</span>,<span class="hljs-number">89</span>,<span class="hljs-number">46</span>] <span class="hljs-keyword">let</span> max = <span class="hljs-built_in">Math</span>.max.apply(<span class="hljs-literal">null</span>,arr)<span class="hljs-comment">//89</span> <span class="hljs-keyword">let</span> min = <span class="hljs-built_in">Math</span>.min.apply(<span class="hljs-literal">null</span>,arr)<span class="hljs-comment">//1</span></code></pre> </li> <li> <p>数组追加</p> <p>数组方法contact比较:contact返回新数组,不修改原数组</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="let arr1 = [1,2,3] let arr2 = [4,5,6] let total = [].push.apply(arr1, arr2) //6" title="" data-original-title="复制"></span> </div> </p></div> <pre class="hljs typescript"><code><span class="hljs-keyword">let</span> arr1 = [<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">3</span>] <span class="hljs-keyword">let</span> arr2 = [<span class="hljs-number">4</span>,<span class="hljs-number">5</span>,<span class="hljs-number">6</span>] <span class="hljs-keyword">let</span> total = [].push.apply(arr1, arr2) <span class="hljs-comment">//6</span></code></pre> </li> <li> <p>利用call和apply做继承</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="function Person(name,age){ // 这里的this都指向实例 this.name = name this.age = age this.sayAge = function(){ console.log(this.age) } } function Female(){ Person.apply(this,arguments)//将父元素所有方法在这里执行一遍就继承了 } let dot = new Female('Dot',2)" title="" data-original-title="复制"></span> </div> </p></div> <pre class="javascript hljs"><code class="javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Person</span>(<span class="hljs-params">name,age</span>)</span>{ <span class="hljs-comment">// 这里的this都指向实例</span> <span class="hljs-keyword">this</span>.name = name <span class="hljs-keyword">this</span>.age = age <span class="hljs-keyword">this</span>.sayAge = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{ <span class="hljs-built_in">console</span>.log(<span class="hljs-keyword">this</span>.age) } } <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Female</span>(<span class="hljs-params"></span>)</span>{ Person.apply(<span class="hljs-keyword">this</span>,<span class="hljs-built_in">arguments</span>)<span class="hljs-comment">//将父元素所有方法在这里执行一遍就继承了</span> } <span class="hljs-keyword">let</span> dot = <span class="hljs-keyword">new</span> Female(<span class="hljs-string">'Dot'</span>,<span class="hljs-number">2</span>)</code></pre> </li> <li> <p>判断变量类型</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="function isArray(obj){ return Object.prototype.toString.call(obj) == '[object Array]' } isArray([]) // true isArray('dot') // false" title="" data-original-title="复制"></span> </div> </p></div> <pre class="javascript hljs"><code class="javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">isArray</span>(<span class="hljs-params">obj</span>)</span>{ <span class="hljs-keyword">return</span> <span class="hljs-built_in">Object</span>.prototype.toString.call(obj) == <span class="hljs-string">'[object Array]'</span> } isArray([]) <span class="hljs-comment">// true</span> isArray(<span class="hljs-string">'dot'</span>) <span class="hljs-comment">// false</span></code></pre> </li> <li> <p>其他:使用 log 代理 console.log</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="function log(){ console.log.apply(console, arguments); } // 当然也有更方便的 let log = console.log()" title="" data-original-title="复制"></span> </div> </p></div> <pre class="hljs javascript"><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">log</span>(<span class="hljs-params"></span>)</span>{ <span class="hljs-built_in">console</span>.log.apply(<span class="hljs-built_in">console</span>, <span class="hljs-built_in">arguments</span>); } <span class="hljs-comment">// 当然也有更方便的 let log = console.log()</span></code></pre> </li> </ul> <h3 id="articleHeader2">bind 实现</h3> <ul> <li> <p>特点:</p> <ol> <li>返回一个函数</li> <li>可以传入参数(使用bind时和bind新生成的函数都可以传参)</li> <li> <p>当 bind 返回的函数作为构造函数的时候,bind 时指定的 <a href="http://www.js-code.com/tag/this" title="this" target="_blank">this</a> 值会失效,但传入的参数依然生效</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="var bindFoo = bar.bind(foo, 'daisy'); var obj = new bindFoo('18');" title="" data-original-title="复制"></span> </div> </p></div> <pre class="javascript hljs"><code class="javascript"><span class="hljs-keyword">var</span> bindFoo = bar.bind(foo, <span class="hljs-string">'daisy'</span>); <span class="hljs-keyword">var</span> obj = <span class="hljs-keyword">new</span> bindFoo(<span class="hljs-string">'18'</span>);</code></pre> </li> </ol> </li> <li>注意:bind这个方法在IE6~8下不兼容</li> </ul> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="// 使用apply和call来实现this指向问题 Function.prototype.bind2 = function (context) { if (typeof this !== &quot;function&quot;) { throw new Error(&quot;what is trying to be bound is not callable&quot;); } var self = this; // 获得bind的参数从第二个参数到最后一个参数 var args = Array.prototype.slice.call(arguments, 1); var fNOP = function () {}; var fBound = function () { // 指bind返回的函数传入的参数 var bindArgs = Array.prototype.slice.call(arguments); // 当作为构造函数时,this 指向实例,此时结果为 true,将绑定函数的 this 指向该实例,可以让实例获得来自绑定函数的值 // 以上面的是 demo 为例,如果改成 `this instanceof fBound ? null : context`,实例只是一个空对象,将 null 改成 this ,实例会具有 habit 属性 // 当作为普通函数时,this 指向 window,此时结果为 false,将绑定函数的 this 指向 context // new bind返回的函数,this失效,但传入的参数生效 return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs)); } // fBound.prototype = this.prototype; // 保证继承,原型链,让 fBound 构造的实例能够继承绑定函数的原型中的值,下面两行代码等同于Object.creater() fbound.prototype = Object.create(this.prototype); // 我们直接修改 fBound.prototype 的时候,也会直接修改绑定函数的 prototype。这个时候,我们可以通过一个空函数来进行中转 fNOP.prototype = this.prototype; fBound.prototype = new fNOP(); return fBound; } // es6实现 Function.prototype.bind = function(context) { if(typeof this !== &quot;function&quot;){ throw new TypeError(&quot;not a function&quot;); } let self = this; let args = [...arguments].slice(1); function Fn() {}; Fn.prototype = this.prototype; let bound = function() { let res = [...args, ...arguments]; //bind传递的参数和函数调用时传递的参数拼接 context = this instanceof Fn ? this : context || this; return self.apply(context, res); } //原型链 bound.prototype = new Fn(); return bound; }" title="" data-original-title="复制"></span> </div> </p></div> <pre class="javascript hljs"><code class="javascript"><span class="hljs-comment">// 使用apply和call来实现this指向问题</span> <span class="hljs-built_in">Function</span>.prototype.bind2 = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">context</span>) </span>{ <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> <span class="hljs-keyword">this</span> !== <span class="hljs-string">"function"</span>) { <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"what is trying to be bound is not callable"</span>); } <span class="hljs-keyword">var</span> self = <span class="hljs-keyword">this</span>; <span class="hljs-comment">// 获得bind的参数从第二个参数到最后一个参数</span> <span class="hljs-keyword">var</span> args = <span class="hljs-built_in">Array</span>.prototype.slice.call(<span class="hljs-built_in">arguments</span>, <span class="hljs-number">1</span>); <span class="hljs-keyword">var</span> fNOP = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{}; <span class="hljs-keyword">var</span> fBound = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{ <span class="hljs-comment">// 指bind返回的函数传入的参数</span> <span class="hljs-keyword">var</span> bindArgs = <span class="hljs-built_in">Array</span>.prototype.slice.call(<span class="hljs-built_in">arguments</span>); <span class="hljs-comment">// 当作为构造函数时,this 指向实例,此时结果为 true,将绑定函数的 this 指向该实例,可以让实例获得来自绑定函数的值</span> <span class="hljs-comment">// 以上面的是 demo 为例,如果改成 `this instanceof fBound ? null : context`,实例只是一个空对象,将 null 改成 this ,实例会具有 habit 属性</span> <span class="hljs-comment">// 当作为普通函数时,this 指向 window,此时结果为 false,将绑定函数的 this 指向 context</span> <span class="hljs-comment">// new bind返回的函数,this失效,但传入的参数生效</span> <span class="hljs-keyword">return</span> self.apply(<span class="hljs-keyword">this</span> <span class="hljs-keyword">instanceof</span> fNOP ? <span class="hljs-keyword">this</span> : context, args.concat(bindArgs)); } <span class="hljs-comment">// fBound.prototype = this.prototype;</span> <span class="hljs-comment">// 保证继承,原型链,让 fBound 构造的实例能够继承绑定函数的原型中的值,下面两行代码等同于Object.creater() fbound.prototype = Object.create(this.prototype);</span> <span class="hljs-comment">// 我们直接修改 fBound.prototype 的时候,也会直接修改绑定函数的 prototype。这个时候,我们可以通过一个空函数来进行中转</span> fNOP.prototype = <span class="hljs-keyword">this</span>.prototype; fBound.prototype = <span class="hljs-keyword">new</span> fNOP(); <span class="hljs-keyword">return</span> fBound; } <span class="hljs-comment">// es6实现</span> <span class="hljs-built_in">Function</span>.prototype.bind = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">context</span>) </span>{ <span class="hljs-keyword">if</span>(<span class="hljs-keyword">typeof</span> <span class="hljs-keyword">this</span> !== <span class="hljs-string">"function"</span>){ <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">TypeError</span>(<span class="hljs-string">"not a function"</span>); } <span class="hljs-keyword">let</span> self = <span class="hljs-keyword">this</span>; <span class="hljs-keyword">let</span> args = [...arguments].slice(<span class="hljs-number">1</span>); <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Fn</span>(<span class="hljs-params"></span>) </span>{}; Fn.prototype = <span class="hljs-keyword">this</span>.prototype; <span class="hljs-keyword">let</span> bound = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{ <span class="hljs-keyword">let</span> res = [...args, ...arguments]; <span class="hljs-comment">//bind传递的参数和函数调用时传递的参数拼接</span> context = <span class="hljs-keyword">this</span> <span class="hljs-keyword">instanceof</span> Fn ? <span class="hljs-keyword">this</span> : context || <span class="hljs-keyword">this</span>; <span class="hljs-keyword">return</span> self.apply(context, res); } <span class="hljs-comment">//原型链</span> bound.prototype = <span class="hljs-keyword">new</span> Fn(); <span class="hljs-keyword">return</span> bound; }</code></pre> <h3 id="articleHeader3">call 实现</h3> <ul> <li> <p>实现思路</p> <ol> <li>将函数设为对象的属性 foo.fn = bar</li> <li>执行该函数 foo.fn()</li> <li>删除该函数 de<a href="http://www.js-code.com/tag/let" title="let" target="_blank">let</a>e foo.fn</li> </ol> </li> <li> <p>注意的点</p> <ol> <li>接受不定长参数 - Arguments 对象中取值,第二个到最后一个参数,然后放到一个数组里</li> <li><a href="http://www.js-code.com/tag/this" title="this" target="_blank">this</a> 参数可以传 <a href="http://www.js-code.com/tag/null" title="null" target="_blank">null</a>,当为 <a href="http://www.js-code.com/tag/null" title="null" target="_blank">null</a> 的时候,视为指向 <a href="http://www.js-code.com/tag/window" title="window" target="_blank">window</a></li> <li>函数是可以有返回值的!</li> </ol> </li> <li> <p>难点解析 - 接受不定长参数</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="<a href="http://www.js-code.com/tag/button" title="button" target="_blank">button</a>" <a href="http://www.js-code.com/tag/class" title="class" target="_blank">class</a>="copyCode code-tool" data-toggle="tooltip" data-placement="<a href="http://www.js-code.com/tag/top" title="top" target="_blank">top</a>" data-clipboard-<a href="http://www.js-code.com/tag/text" title="text" target="_blank">text</a>="<a href="http://www.js-code.com/tag/var" title="var" target="_blank">var</a> args = [];<br /> // 为了拼出一个参数字符串,<a href="http://www.js-code.com/tag/arguments" title="arguments" target="_blank">arguments</a>类数组,不能使用<br /> <a href="http://www.js-code.com/tag/for" title="for" target="_blank">for</a>(<a href="http://www.js-code.com/tag/var" title="var" target="_blank">var</a> i = 1, len = <a href="http://www.js-code.com/tag/arguments" title="arguments" target="_blank">arguments</a>.length; i < len; i++) { // args: [&quot;arguments[1]&quot;, &quot;arguments[2]&quot;, .....] args.push('arguments[' + i + ']'); } // 1. con<a href="http://www.js-code.com/tag/text" title="text" target="_blank">text</a>.fn(args.join(',')) <a href="http://www.js-code.com/tag/es6" title="es6" target="_blank">es6</a>语法实现es3的call方法不合适<br /> // 2. 这里 args 会自动调用 <a href="http://www.js-code.com/tag/Array" title="Array" target="_blank">Array</a>.<a href="http://www.js-code.com/tag/toString" title="toString" target="_blank">toString</a>() 这个方法<br /> // 3. <a href="http://www.js-code.com/tag/eval" title="eval" target="_blank">eval</a>作用:看成是<script>标签,只接受原始字符串作为参数,用JavaScript的解析引擎来解析这一堆字符串里面的内容<br /> var result = eval('context.fn(' + args +')');</p> <p>" title="" data-original-title="复制"></span> </div> </p></div> <pre class="javascript hljs"><code class="javascript"><span class="hljs-keyword">var</span> args = []; <span class="hljs-comment">// 为了拼出一个参数字符串,arguments类数组,不能使用</span> <span class="hljs-keyword">for</span>(<span class="hljs-keyword">var</span> i = <span class="hljs-number">1</span>, len = <span class="hljs-built_in">arguments</span>.length; i &lt; len; i++) { <span class="hljs-comment">// args: ["arguments[1]", "arguments[2]", .....]</span> args.push(<span class="hljs-string">'arguments['</span> + i + <span class="hljs-string">']'</span>); } <span class="hljs-comment">// 1. context.fn(args.join(',')) es6语法实现es3的call方法不合适</span> <span class="hljs-comment">// 2. 这里 args 会自动调用 Array.toString() 这个方法</span> <span class="hljs-comment">// 3. eval作用:看成是&lt;script&gt;标签,只接受原始字符串作为参数,用JavaScript的解析引擎来解析这一堆字符串里面的内容</span> <span class="hljs-keyword">var</span> result = <span class="hljs-built_in">eval</span>(<span class="hljs-string">'context.fn('</span> + args +<span class="hljs-string">')'</span>); </code></pre> </li> <li> <p>call整体实现</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="<a href="http://www.js-code.com/tag/button" title="button" target="_blank">button</a>" <a href="http://www.js-code.com/tag/class" title="class" target="_blank">class</a>="copyCode code-tool" data-toggle="tooltip" data-placement="<a href="http://www.js-code.com/tag/top" title="top" target="_blank">top</a>" data-clipboard-text="Function.<a href="http://www.js-code.com/tag/prototype" title="prototype" target="_blank">prototype</a>.call2 = <a href="http://www.js-code.com/tag/function" title="function" target="_blank">function</a> (context) {<br /> // 首先要获取调用call的函数,用<a href="http://www.js-code.com/tag/this" title="this" target="_blank">this</a>可以获取<br /> var context = context || win<a href="http://www.js-code.com/tag/do" title="do" target="_blank">do</a>w;<br /> context.fn = <a href="http://www.js-code.com/tag/this" title="this" target="_blank">this</a>;<br /> var args = [];<br /> <a href="http://www.js-code.com/tag/for" title="for" target="_blank">for</a>(var i = 1, len = arguments.length; i < len; i++) { args.push('arguments[' + i + ']'); } var result = eval('context.fn(' + args +')'); delete context.fn return result; } // 测试 bar.call2(null); // 2 console.log(bar.call2(obj, 'kevin', 18)); // es6 Function.prototype.call = function (context) { if (!context) { context = typeof window === 'undefined' ? global : window; } context.fn = this; let rest = [...arguments].slice(1);// 空数组slice后返回的仍然是空数组 let result = context.fn(...rest); delete context.fn; return result; } " title="" data-original-title="复制"></span> </div> </p></div> <pre class="javascript hljs"><code class="javascript"><span class="hljs-built_in">Function</span>.prototype.call2 = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">context</span>) </span>{ <span class="hljs-comment">// 首先要获取调用call的函数,用this可以获取</span> <span class="hljs-keyword">var</span> context = context || <span class="hljs-built_in">window</span>; context.fn = <span class="hljs-keyword">this</span>; <span class="hljs-keyword">var</span> args = []; <span class="hljs-keyword">for</span>(<span class="hljs-keyword">var</span> i = <span class="hljs-number">1</span>, len = <span class="hljs-built_in">arguments</span>.length; i &lt; len; i++) { args.push(<span class="hljs-string">'arguments['</span> + i + <span class="hljs-string">']'</span>); } <span class="hljs-keyword">var</span> result = <span class="hljs-built_in">eval</span>(<span class="hljs-string">'context.fn('</span> + args +<span class="hljs-string">')'</span>); <span class="hljs-keyword">delete</span> context.fn <span class="hljs-keyword">return</span> result; } <span class="hljs-comment">// 测试</span> bar.call2(<span class="hljs-literal">null</span>); <span class="hljs-comment">// 2</span> <span class="hljs-built_in">console</span>.log(bar.call2(obj, <span class="hljs-string">'kevin'</span>, <span class="hljs-number">18</span>)); <span class="hljs-comment">// es6</span> <span class="hljs-built_in">Function</span>.prototype.call = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">context</span>) </span>{ <span class="hljs-keyword">if</span> (!context) { context = <span class="hljs-keyword">typeof</span> <span class="hljs-built_in">window</span> === <span class="hljs-string">'undefined'</span> ? global : <span class="hljs-built_in">window</span>; } context.fn = <span class="hljs-keyword">this</span>; <span class="hljs-keyword">let</span> rest = [...arguments].slice(<span class="hljs-number">1</span>);<span class="hljs-comment">// 空数组slice后返回的仍然是空数组</span> <span class="hljs-keyword">let</span> result = context.fn(...rest); <span class="hljs-keyword">delete</span> context.fn; <span class="hljs-keyword">return</span> result; } </code></pre> </li> </ul> <h3 id="articleHeader4">apply实现</h3> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="Function.<a href="http://www.js-code.com/tag/prototype" title="prototype" target="_blank">prototype</a>.apply = <a href="http://www.js-code.com/tag/function" title="function" target="_blank">function</a> (context, arr) {<br /> var context = <a href="http://www.js-code.com/tag/Object" title="Object" target="_blank">Object</a>(context) || <a href="http://www.js-code.com/tag/window" title="window" target="_blank">window</a>;<br /> context.fn = <a href="http://www.js-code.com/tag/this" title="this" target="_blank">this</a>;<br /> var result;<br /> <a href="http://www.js-code.com/tag/if" title="if" target="_blank">if</a> (!arr) {<br /> result = context.fn();<br /> }<br /> <a href="http://www.js-code.com/tag/else" title="else" target="_blank">else</a> {<br /> var args = [];<br /> for (var i = 0, len = arr.length; i < len; i++) { args.push('arr[' + i + ']'); } result = eval('context.fn(' + args + ')') } delete context.fn return result; } // es6: Function.prototype.apply = function (context, rest) { if (!context) { //context为null或者是undefined时,设置默认值 context = typeof window === 'undefined' ? global : window; } context.fn = this; let result; if(rest === undefined || rest === null) { //undefined 或者 是 null 不是 Iterator 对象,不能被 ... result = context.fn(rest); }else if(typeof rest === 'object') { result = context.fn(...rest); } delete context.fn; return result; } " title="" data-original-title="复制"></span> </div> </p></div> <pre class="javascript hljs"><code class="javascript"><span class="hljs-built_in">Function</span>.prototype.apply = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">context, arr</span>) </span>{ <span class="hljs-keyword">var</span> context = <span class="hljs-built_in">Object</span>(context) || <span class="hljs-built_in">window</span>; context.fn = <span class="hljs-keyword">this</span>; <span class="hljs-keyword">var</span> result; <span class="hljs-keyword">if</span> (!arr) { result = context.fn(); } <span class="hljs-keyword">else</span> { <span class="hljs-keyword">var</span> args = []; <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>, len = arr.length; i &lt; len; i++) { args.push(<span class="hljs-string">'arr['</span> + i + <span class="hljs-string">']'</span>); } result = <span class="hljs-built_in">eval</span>(<span class="hljs-string">'context.fn('</span> + args + <span class="hljs-string">')'</span>) } <span class="hljs-keyword">delete</span> context.fn <span class="hljs-keyword">return</span> result; } <span class="hljs-comment">// es6:</span> <span class="hljs-built_in">Function</span>.prototype.apply = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">context, rest</span>) </span>{ <span class="hljs-keyword">if</span> (!context) { <span class="hljs-comment">//context为null或者是undefined时,设置默认值</span> context = <span class="hljs-keyword">typeof</span> <span class="hljs-built_in">window</span> === <span class="hljs-string">'undefined'</span> ? global : <span class="hljs-built_in">window</span>; } context.fn = <span class="hljs-keyword">this</span>; <span class="hljs-keyword">let</span> result; <span class="hljs-keyword">if</span>(rest === <span class="hljs-literal">undefined</span> || rest === <span class="hljs-literal">null</span>) { <span class="hljs-comment">//undefined 或者 是 null 不是 Iterator 对象,不能被 ...</span> result = context.fn(rest); }<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(<span class="hljs-keyword">typeof</span> rest === <span class="hljs-string">'object'</span>) { result = context.fn(...rest); } <span class="hljs-keyword">delete</span> context.fn; <span class="hljs-keyword">return</span> result; } </code></pre> <h3 id="articleHeader5">补充:</h3> <ul> <li><a href="http://www.js-code.com/tag/new" title="new" target="_blank">new</a>实现</li> <li>继承和原型链知识</li> </ul> <p></code></p>
脚本宝典为你提供优质服务
脚本宝典 » 详解call bind apply – 区别/使用场景/es6实现/es3实现

发表评论

提供最优质的资源集合

立即查看 了解详情