Giter VIP home page Giter VIP logo

Comments (217)

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024 121

@jawil 感谢回答哈~
@fantasy123 最终目的是为了拼出一个参数字符串,我们一步一步看:

var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
        args.push('arguments[' + i + ']');
}

最终的数组为:

var args = [arguments[1], arguments[2], ...]

然后

 var result = eval('context.fn(' + args +')');

在eval中,args 自动调用 args.toString()方法,eval的效果如 jawil所说,最终的效果相当于:

 var result = context.fn(arguments[1], arguments[2], ...);

这样就做到了把传给call的参数传递给了context.fn函数

from blog.

jawil avatar jawil commented on May 2, 2024 109

eval函数接收参数是个字符串

定义和用法

eval() 函数可计算某个字符串,并执行其中的的 JavaScript 代码。

语法:
eval(string)

string必需。要计算的字符串,其中含有要计算的 JavaScript 表达式或要执行的语句。该方法只接受原始字符串作为参数,如果 string 参数不是原始字符串,那么该方法将不作任何改变地返回。因此请不要为 eval() 函数传递 String 对象来作为参数。

简单来说吧,就是用JavaScript的解析引擎来解析这一堆字符串里面的内容,这么说吧,你可以这么理解,你把eval看成是<script>标签。

eval('function Test(a,b,c,d){console.log(a,b,c,d)};Test(1,2,3,4)')
@fantasy123

from blog.

hujiulong avatar hujiulong commented on May 2, 2024 19

context.fn = this;这里似乎漏掉了一个很关键的问题,如果context本来就有fn这个成员怎么办。这里只能给一个原来不存在的名字

var id = 0;
while ( context[ id ] ) {
    id ++;
}
context[ id ] = this;

不过这个方法似乎有点傻

from blog.

JuniorTour avatar JuniorTour commented on May 2, 2024 15

受益匪浅!学到了很多,谢谢前辈!
有一个小问题:call2第三版和apply的函数内,是不是不必要 var context =...,直接context=...即可?

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024 15

@Ortonzhang 可以再简化一点:

Function.prototype.call2 = function (context, ...args) {
    context = context || window
    context.__fn__ = this
    let result = context.__fn__(...args)
    delete context.__fn__
    return result
}

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024 15

@Arvinzhu 哈哈,感谢分享哈~ 确实这个更加严谨一些~ 而且既然已经用了 ES6 的 Symbol 和 Reflect,可以再简化一点,用上拓展运算符:

    Function.prototype.call2 = function(context, ...args) {
        context = context || window;
        let fn = Symbol()
        context[fn] = this;
        var result =  context[fn](...args)
        Reflect.deleteProperty(context, fn)
        return result;
    }

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024 14

哈哈,确实可以,没有注意到这点,感谢指出,(๑•̀ㅂ•́)و✧

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024 10

@yvanwangl 当然不可以啦,举个例子:

Function.prototype.apply2 = function (context, arr) {
    var context = Object(context) || window;
    context.fn = this;

    var result;
    if (!arr) {
        result = context.fn();
    }
    else {
        // var args = [];
        // for (var i = 0, len = arr.length; i < len; i++) {
        //     args.push('arr[' + i + ']');
        // }
        // result = eval('context.fn(' + args + ')')

        result = eval('context.fn(' + arr+ ')')
    }

    delete context.fn
    return result;
}

// 测试一下
var foo = {
    value: 1
};

function bar() {
    console.log(this.value);
}

bar.apply2(foo, ['kevin']); // ReferenceError: kevin is not defined

如果直接使用 arr,比如 arr 为 ['kevin'],eval 会直接解析 'kevin' 为一个变量,从而导致报错~

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024 9

@main2017 举个例子:

var a = 'kevin';
function fn(o) {
     console.log(o)
};
eval('fn(' +  a + ')');

在这个例子下, eval('fn(' + a + ')'); 相当于执行 fn(kevin),注意不是 fn('kevin'),kevin 为一个变量,就会报错

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024 9

@ClarenceC 举个例子哈:

function Person() {
  this.name = 'ClarenceC';
}
 
Person.prototype.greet = function(){
  console.log('hello ' + this.name);
}

var person = new Person();
console.log(person.name)

person.greet()

这个例子是想说明 person 作为构造函数 Person 的实例对象,person.greet 函数中的 this 是指向实例对象 person 的。

其实这个也是一样的道理:

Function.prototype.call2 = function(context) {
    context.fn = this;
    context.fn();
    delete context.fn;
}

每个函数都是 构造函数 Function 的实例对象,bar.call 或者 bar.apply 函数中的 this 自然也是指向该实例对象即 bar 的~

from blog.

btea avatar btea commented on May 2, 2024 6

@geekzhanglei
args.join(','),你这步操作得到的结果是一个由传入的所有参数拼接而成的字符串,相当于只传入一个数据类型是字符串的参数,最后函数执行结果肯定和你预期的不一样。

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024 4

@lz-lee call 的实现中,是通过 arguments 取各个参数,所以从 1 开始,省略掉为 0 的 context,而apply的实现中,arr 直接就表示参数的数组,循环这个参数数组,直接就从 0 开始。

from blog.

chenjigeng avatar chenjigeng commented on May 2, 2024 4
Function.prototype.myCall = function (context, ...args) {
  context = context || window;
  const fnName = Symbol('fn');
  context[fnName] = this;
  const result = context[fnName](...args);
  delete context[fnName];

  return result;
}

from blog.

jasperchou avatar jasperchou commented on May 2, 2024 3
// 以上个例子为例,此时的arguments为:
// arguments = {
//      0: foo,
//      1: 'kevin',
//      2: 18,
//      length: 3
// }
// 因为arguments是类数组对象,所以可以用for循环
var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
    args.push('arguments[' + i + ']');
}

// 执行后 args为 [foo, 'kevin', 18]

// 执行后 args为 [foo, 'kevin', 18]

这一句可能造成误导。结果为:["arguments[1]", "arguments[2]"]
虽然后面确实会用eval执行,但是此处还没有。

from blog.

Arvinzhu avatar Arvinzhu commented on May 2, 2024 3
Function.prototype.call2 = function(context){
  context = context || window;
  let bake = context.fn === undefined? undefined :context.fn ;
  let fn=Symbol()
  context[fn] = this;
  let args = []
  for(var i =1 ,len = arguments.length;i<len;i++){
    args.push('arguments['+i+']')
  }
  let str = args.join(',')
  eval('context[fn]('+str+')');
  Reflect.deleteProperty(context,fn)
}


可以使用Symbol,这样就不用担心会覆盖context的属性了

from blog.

hongzzz avatar hongzzz commented on May 2, 2024 3

@TothingWay 你根本没理解eval的用法啊

function fn(name) {
    console.log('my name is ' + name);
}

let name = 'hello';

eval('fn(' + 'name' + ')');
eval('fn(' + name + ')');

这两行你分别跑一下试试? 实践出真知

from blog.

fantasy123 avatar fantasy123 commented on May 2, 2024 2

arr.push('arguments['+i+']');
请问这里为什么是一个拼接操作呢?

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024 2

@lzr900515 没有什么问题哈,非严格模式下,指定为 null 或 undefined 时会自动指向全局对象,郑航写的是严格模式下的,我写的是非严格模式下的,实际上现在的模拟代码有一点没有覆盖,就是当值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的自动包装对象。

from blog.

btea avatar btea commented on May 2, 2024 2

@zdliuccit 上面 @mqyqingfeng 有介绍过
eval('context.fn(' + ary + ')'),ary会自动调用ary.toString(),这个结果和你ary.join(',')一样。
假如ary=['kevin'],eval效果等同于context.fn(kevin),会报错kevin is not defined...

from blog.

FightingHao avatar FightingHao commented on May 2, 2024 2
Function.prototype.call2 = function (context, ...args) {
  var glo = typeof window === 'object' ? window : global
  context = context || glo
  context.fn = this
  var result = context.fn(...args)
  delete context.fn
  return result
}

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024 1

@BeijiYang 例子中的展开效果的例子是这样的:

eval('context.fn(' + args +')')

数组的展开效果与 eval 无关,是隐式类型导致的,我们来看一个例子:

console.log('1' + [1, 2, 3]) // 11,2,3

from blog.

TothingWay avatar TothingWay commented on May 2, 2024 1
Function.prototype.apply = function (context, arr) {
    var context = Object(context) || window;
    context.fn = this;
    var result;
    if (!arr) {
        result = context.fn();
    }
    else {
        var args = [];
        for (var i = 0, len = arr.length; i < len; i++) {
            args.push('arr[' + i + ']');
        }
        result = eval('context.fn(' + args + ')')
    }
    delete context.fn
    return result;
}

arr这个参数本身就是一个参数数组,为什么要循环放入新的数组然后再传入函数?直接在 else 中
result = eval('context.fn(' + arr + ')')
这样有什么问题吗?

from blog.

qin9smile avatar qin9smile commented on May 2, 2024 1

@magicstf 比如说 有个方法

function example(ar1, ar2) {
console.log(arguments);
}

// 调用的时候是这么调的
example("1", "2");  // 输出 {0: "1", 1: "2", length: 2}

// 回到你说的
var result = context.fn(args); // 基于前面的代码 args是一个数组。
var result = context.fn([1,2,3]) // 实际上如果传值的话是跟这行一样的。而call的调用方式是按照(ar1, ar2)这么来的。

// 再来说eval eval方法接收字符串作为脚本来执行。也就是说,如果接收的参数不是字符串,会强转为字符串。
// args 作为数组,调用内部的Object.toString()方法。比如说 [1,2,3,4,5] 转换为字符串后就是"1,2,3,4,5"
eval("example(1,2,3,4,5)")//这样不就正好了嘛

然后说 为什么要用eval方法 看了些别的文章 追溯到ES5-shim 有一段这样的

// XXX Build a dynamic function with desired amount of arguments is the only
// way to set the length property of a function.
// In environments where Content Security Policies enabled (Chrome extensions,
// for ex.) all use of eval or Function costructor throws an exception.
// However in all of these environments Function.prototype.bind exists
// and so this code will never be executed.
bound = $Function('binder', 'return function (' + array_join.call(boundArgs, ',') + '){ return binder.apply(this, arguments); }')(binder);

就是说 函数有个length属性,返回的是函数arguments的length。这个属性是只读的,只能用动态创建函数的方式,才能保证length是对的。 ES5-shim里面用创建Function的方式,博主给的实现是用eval的方式。

from blog.

lz-lee avatar lz-lee commented on May 2, 2024

apply郑航的实现,循环是不是应该从 i = 1 开始?

from blog.

lz-lee avatar lz-lee commented on May 2, 2024

粗心大意了。感谢提醒。@mqyqingfeng

from blog.

lynn1824 avatar lynn1824 commented on May 2, 2024

分析的很透彻,点个赞!

from blog.

qianlongo avatar qianlongo commented on May 2, 2024

赞赞赞

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@lynn1824 @qianlongo 感谢夸奖,写的时候我就觉得模拟实现一遍 call 和 apply 最能让大家明白 call 和 apply 的原理 (~ ̄▽ ̄)~

from blog.

lzr900515 avatar lzr900515 commented on May 2, 2024

var context = Object(context) || window; 这里有问题吗?context为null时Object(null)返回空对象,不会被赋值为window

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@hujiulong 哈哈,有道理哈~ 确实会覆盖之前对象的方法,还好模拟实现 call 和 apply 的目的在于让大家通过模拟实现了解 call 和 apply 的原理,实际开发的时候还是要直接使用 call 和 apply 的~

from blog.

libin1991 avatar libin1991 commented on May 2, 2024

实在是佩服!

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@jasperchou 感谢指出~ 这里确实写错了 o( ̄▽ ̄)d

from blog.

ry928330 avatar ry928330 commented on May 2, 2024

apply的实现,如果传入的第二个参数不是一个数组,原生的apply是会报错的。比如bar.apply(foo, 1)。

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@ry928330 感谢指出哈~ 第二个参数除了数组之外,还支持类数组对象~

from blog.

shaodahong avatar shaodahong commented on May 2, 2024
context.fn = this;

如果这个fn是有的话会被覆盖掉然后被delete掉了吧

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@shaodahong 确实会发生这种情况,不过本篇的目的其实是想让大家通过模拟实现 call 和 apply 了解 call 和 apply 的原理,所以这种情况就不特别处理了~

from blog.

yvanwangl avatar yvanwangl commented on May 2, 2024

apply 的实现直接 result = eval('context.fn(' + arr+ ')') 不得了么。

from blog.

yvanwangl avatar yvanwangl commented on May 2, 2024

@mqyqingfeng 确实,应该在运行时取值。

from blog.

main2018 avatar main2018 commented on May 2, 2024

@mqyqingfeng 如果直接使用 arr,比如 arr 为 ['kevin'],eval 会直接解析 'kevin' 为一个变量,从而导致报错~, [这是为什么]

from blog.

main2018 avatar main2018 commented on May 2, 2024

@mqyqingfeng kevin和arr[0]不都一样吗,

from blog.

main2018 avatar main2018 commented on May 2, 2024

@mqyqingfeng 哦哦,素戴斯乃,灰常感谢,赞赞

from blog.

CodeLittlePrince avatar CodeLittlePrince commented on May 2, 2024

一级棒

from blog.

ClarenceC avatar ClarenceC commented on May 2, 2024

厉害了,学习了.回到es3的eval方法,我想不到还有什么方法,可以把数组转为函数args.除了es6,call,apply,bind外.
还有context.fn = this; 为什么方法里面调用方法,的this 为 方法本身呢,这里能解释一下吗?我一开始以为this是 windown 对象.

from blog.

ClarenceC avatar ClarenceC commented on May 2, 2024

谢谢冴羽大大,懂了.bar 是 Function 的实例对象.所以方法的this 是返回实例bar.

from blog.

crowphy avatar crowphy commented on May 2, 2024

如果是在node中,call的第一个参数为null。。。怎么办?因为node的模块化,无法通过global或者module访问当前模块的的变量。。。

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@crowphy 嘿嘿……没有明白什么意思?能写一段 demo 不?

from blog.

crowphy avatar crowphy commented on May 2, 2024

在 node 中试试bar.call(null, 123);这样调用,会直接报window is not defined。我的意思是在 node 中没有 window 对象,但是 global 是无法访问当前模块的变量的,所以我的问题是如何去兼容 node 环境?不知道这样说是否明白。。。

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@crowphy 在 node 环境下,global 就是无法访问当前模块的变量的,比如你在 node 中运行原生的这段代码:

var b = 1;
console.log(global.b); // undefined

使用 call 的时候:

var value = 2;

function bar(name, age) {
    return {
        value: this.value,
        name: name,
        age: age
    }
}

var a = bar.call(null);
console.log(a); // { value: undefined }

值也是 undefined 的,node 环境就是这样的,不能像浏览器环境下使用 this.value 就能访问全局声明的变量,所以如果做 node 下的 call,将 window 替换成 global 就可以了。

from blog.

Ortonzhang avatar Ortonzhang commented on May 2, 2024

受益匪浅,试写了下es6版本的,欢迎指正。

Function.prototype.call2 = function(context){
    var context = context || window 
    context.__fn__ = this
    let result = context.__fn__(...Array.from(arguments).slice(1))
    delete context.__fn__
    return result
}

from blog.

ZhengLiJing avatar ZhengLiJing commented on May 2, 2024

http://jsbin.com/juhehil/10 ,为什么打印出的结果是3和1?求解,我参考的是知乎@郑航的写法。

from blog.

delayk avatar delayk commented on May 2, 2024

@ZhengLiJing
你的context是foo, 所以第一行打印3.
result = eval("context.fn(" + arg + ")");//关键在于字符串拼接这里,相当于arg+""
这句话相当于执行了context.fn(arr[0], arr[1], arr[2]),bar的第二行打印的其实都是arr[0]

from blog.

ZhengLiJing avatar ZhengLiJing commented on May 2, 2024

@delayk 理解了,非常感谢。

from blog.

HuangQiii avatar HuangQiii commented on May 2, 2024

每看一次就有不一样的收获,已经很理解怎么实现的了~

还有个小笔误,第三版里验证这里应该是想表达call2吧

bar.call(null); // 2

from blog.

geekzhanglei avatar geekzhanglei commented on May 2, 2024

为什么不是这样的呢?

Function.prototype.call2 = fucntion(context) {
    var args = [];
    for (var i = 1, len = arguments.length; i < len; i++) {
        args.push(arguments[i]);
    }
    context.__fn = this || window;
    var result = context.__fn(args.join(','));
    delete context.__fn;
    return result;
}

为什么一定要eval函数解析下?

from blog.

geekzhanglei avatar geekzhanglei commented on May 2, 2024

@btea 嗯嗯,是的,疏忽了,感谢。

from blog.

nickyzhang-fe avatar nickyzhang-fe commented on May 2, 2024

内容和评论一样精彩

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@HuangQiii 感谢指出哈~

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@btea 感谢回答~ 😀

from blog.

sxzy avatar sxzy commented on May 2, 2024

为什么需要用eval呢?就是我能用下面这种吗?直接用扩展符展开呢???

Function.prototype.call = function(context) {
  var context = context || window;
  context.fn = this;
  var args = Array.prototype.slice.call(arguments,1);
  var result = context.fn(...args);
  delete context.fn
  return result;
}

from blog.

sxzy avatar sxzy commented on May 2, 2024

哈哈哈哈,看到了。我这个是用es6来写

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@sxzy 当然可以啦,上面的评论中有 ES6 的版本哦~

from blog.

BeijiYang avatar BeijiYang commented on May 2, 2024

很好的文章,感谢。
同时有一点不太没明白,请教一下:为什么在 eval() 参数中使用变量,会有数组展开的效果?

例如

let a = 1,
  b = 2,
  c = 3;

let arr = [a, b, c];

function test(p1, p2, p3) {
  console.log(`${p1} ~ ${p2} ~ ${p3} ~ `);
}

test(...arr);     //1 ~ 2 ~ 3 ~
eval(`test(${arr})`);     //1 ~ 2 ~ 3 ~
test(arr);    //1,2,3 ~~~ undefined ~~~ undefined ~~~
test(arr.toString());    //1,2,3 ~~~ undefined ~~~ undefined ~~~

@mqyqingfeng

from blog.

BeijiYang avatar BeijiYang commented on May 2, 2024

感谢大神回复。
我是不明白为什么 args 这个数组整体没有被当成第一个参数 arguments[0],
而是将数组内部的元素 args[0] 作为 arguments[0] ,args[1] 作为 arguments[1]。

以第二版代码为例,

context.fn(args); 

就是第一种情况,输出

[ 'arguments[1]', 'arguments[2]' ]
undefined
1


eval("context.fn(" + args + ")")
就是正确情况,输出

kevin
18
1

就是不明白这里头 eval() 起的作用

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@BeijiYang

假设 args 为:

var args = [arguments[1], arguments[2]]

然后

eval('context.fn(' + args +')');

传入 eval 函数中的字符串为:

'context.fn(arguments[1], arguments[2])'

eval 就解析为:

context.fn(arguments[1], arguments[2])

不知道这样解释清晰不?

from blog.

BeijiYang avatar BeijiYang commented on May 2, 2024

清楚了!刚刚钻牛角尖了。感谢大神@mqyqingfeng ,期待大神新文章。

from blog.

johnsken-jerry avatar johnsken-jerry commented on May 2, 2024

超级棒,今天算是加深了对call、apply的了解。
eval一直困扰着我 @mqyqingfeng 感谢大神

from blog.

thereisnowinter avatar thereisnowinter commented on May 2, 2024
var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
        args.push('arguments[' + i + ']');
}
最终的数组为:

var args = [arguments[1], arguments[2], ...]

var args 不是应该是['argumets[1]', 'arguments[2]',...]吗?希望大佬能解答一下?

from blog.

Luoyuda avatar Luoyuda commented on May 2, 2024

终于理解这两个东西,其他的教程都讲的云里雾里的,醉了

from blog.

EthanLin-TWer avatar EthanLin-TWer commented on May 2, 2024

讲得很好,点个赞。问一下,实践中使用 apply 的场景有什么呢?感觉在写业务代码时,this 的指向一般都是确定的,参数也可以在 ES6 的参数解构出来以后得到解决。

我能想到有两个场景,一个是写框架,一个是做柯里化。不过自己尝试了一下,写柯里化其实可以不用用到 apply,而写框架什么场景下会需要用到 apply,我不是很确定。或者,是不是还有其他我没想到的使用场景呢?能否麻烦楼主解答一下呢,感谢。

from blog.

nickyzhang-fe avatar nickyzhang-fe commented on May 2, 2024

你好,在Function.prototype.apply = function(context, arr)这个实现方法中,将数组用for循环遍历,再使用eval包装,是考虑到类数组的情况吗?还是有其他的考虑,烦请讲解

from blog.

wojiaofengzhongzhuifeng avatar wojiaofengzhongzhuifeng commented on May 2, 2024
// 第一版
Function.prototype.call2 = function(context) {
    // 首先要获取调用call的函数,用this可以获取
    context.fn = this;
    context.fn();
    delete context.fn;
}

// 测试一下
var foo = {
    value: 1
};

function bar() {
    console.log(this.value);
}

bar.call2(foo); // 1

为什么能用this获取bar函数??
@mqyqingfeng

from blog.

xsfxtsxxr avatar xsfxtsxxr commented on May 2, 2024
// 第一版
Function.prototype.call2 = function(context) {
    // 首先要获取调用call的函数,用this可以获取
    context.fn = this;
    context.fn();
    delete context.fn;
}

// 测试一下
var foo = {
    value: 1
};

function bar() {
    console.log(this.value);
}

bar.call2(foo); // 1

为什么能用this获取bar函数??

@mqyqingfeng

Function是构造函数,this指向实例化的对象function bar。

from blog.

wd2010 avatar wd2010 commented on May 2, 2024

@Arvinzhu 哈哈,感谢分享哈~ 确实这个更加严谨一些~ 而且既然已经用了 ES6 的 Symbol 和 Reflect,可以再简化一点,用上拓展运算符:

 Function.prototype.call2 = function(context, ...args) {
     context = context || window;
     let fn = Symbol()
     context[fn] = this;
     var result =  context[fn](...args)
     Reflect.deleteProperty(context, fn)
     return result;
 }

@mqyqingfeng 突然发现在call和apply中的第一个参数可以为string或其他的原始类型,所以context = context || window;是不是要把context变为对象类型 context=context ? Object(context): window, 不然后面执行context[fn](...args)时会报错。

var foo=()=>1
foo.call('a'); // 返回1,没有报错

from blog.

jianxinWan avatar jianxinWan commented on May 2, 2024

call中如果参数传入一个基本类型的值,这个方法会对传入的值进行包装

function bar(){
         console.log(this);
         console.log(this.name);
         console.log(this.age);
}
bar.call('232313');

String {"232313"}

感觉这块也得处理一下,做一个类型判断

from blog.

dingsheng1214 avatar dingsheng1214 commented on May 2, 2024

Function.prototype.call2 = function(context,...args){
context = context || window
var fn = Symbol()
context[fn] = this
contextfn
delete context.fn
}

from blog.

yygmind avatar yygmind commented on May 2, 2024

笔者最后 apply 的实现中 var context = Object(context) || window; 好像有点问题,如果执行 bar.apply(null),输出为 undefined,理论上应该是输出2,这边 context 为什么要转成 Object 呢?

from blog.

hezhongfeng avatar hezhongfeng commented on May 2, 2024

@TothingWay
这里是想拼一些字符串,args实际上等于["arr[0]", "arr[1]", "arr[2]"],不是把arr换个引用,为了给eval使用

from blog.

TothingWay avatar TothingWay commented on May 2, 2024

@hezhongfeng arr 不是直接可以给 eval 使用么?

from blog.

hezhongfeng avatar hezhongfeng commented on May 2, 2024

@TothingWay
肯定是不行的了,上面有eval的详细用法,你可以试试,并不推荐使用这个api

from blog.

TothingWay avatar TothingWay commented on May 2, 2024

why?上面的 eval 用法我看了,我只是觉得没必要 push 进一个新的 args 数组中,arr 本身就是一个参数数组,直接给 eval 使用,程序也能正常执行

from blog.

TothingWay avatar TothingWay commented on May 2, 2024

@hongzzz oh,明白了, 谢谢

from blog.

hezhongfeng avatar hezhongfeng commented on May 2, 2024

@TothingWay 自己试一下,别光说啊,我看你不明白 eval 用法,还在那说可以正常执行?你执行下试试?

from blog.

TothingWay avatar TothingWay commented on May 2, 2024

@hezhongfeng 已经明白了,之前应该是写错了,抱歉,也谢谢你的解答

from blog.

btea avatar btea commented on May 2, 2024

@zdliuccit 直接用join是不行的。你这里join(',')拼接的结果和直接使用ary是一样的,都有问题。

from blog.

zdliuccit avatar zdliuccit commented on May 2, 2024

@btea 求解

from blog.

zdliuccit avatar zdliuccit commented on May 2, 2024

@btea 了解,thanks

from blog.

EvalGitHub avatar EvalGitHub commented on May 2, 2024

apply的实现中,参数已经是数组的形式了,为啥还用for循环???

from blog.

minzhang2 avatar minzhang2 commented on May 2, 2024

call的实现如果不借助es6语法的话,我觉得先用var args = Array.prototype.slice.call(arguments, 1);获取后面的参数,最后用eval('ctx.fn(' + JSON.stringify(args).replace(/^[|]$/g,'') + ')');这样保证参数如果是数组或者对象都可以运行

Function.prototype.call1 = function(ctx) {
	var args = Array.prototype.slice.call(arguments, 1);
	ctx = Object(ctx);
	ctx.fn = this;
	eval('ctx.fn(' + JSON.stringify(args).replace(/^\[|\]$/g,'') + ')');
        delete ctx.fn
}

from blog.

magicstf avatar magicstf commented on May 2, 2024

apply的模拟实现 没必要把传入的数组再遍历一遍再push 到新数组吧?

from blog.

qin9smile avatar qin9smile commented on May 2, 2024

apply的模拟实现 没必要把传入的数组再遍历一遍再push 到新数组吧?

@magicstf 传进来的arguments是类数组,直接使用其实传递的应该是 一个对象,key是数组的下标

from blog.

magicstf avatar magicstf commented on May 2, 2024

var rusult = context.fn(args) 这样也ok, 为什么要eval 呢

from blog.

liuxiumei123 avatar liuxiumei123 commented on May 2, 2024
// 第一版
Function.prototype.call2 = function(context) {
    // 首先要获取调用call的函数,用this可以获取
    context.fn = this;
    context.fn();
    delete context.fn;
}

// 测试一下
var foo = {
    value: 1
};

function bar() {
    console.log(this.value);
}

bar.call2(foo); // 1

为什么能用this获取bar函数??
@mqyqingfeng

因为是bar函数对象调用的call2方法,方法中的this指向调用方法的对象,即bar函数对象

from blog.

KingJeason avatar KingJeason commented on May 2, 2024
var result = new Function('context', 'arguments', 'context.fn(' + args + ')')(context, arguments)

用Function的形式个人觉得更好理解一些哈哈

from blog.

jack-hate-titanic avatar jack-hate-titanic commented on May 2, 2024

args.push('arguments[' + i + ']');这里不用拼接操作也是可以的,但是eval是要用的,eval解析数组后会为一个一个逗号相隔的参数

from blog.

KingJeason avatar KingJeason commented on May 2, 2024

args.push('arguments[' + i + ']');这里不用拼接操作也是可以的,但是eval是要用的,eval解析数组后会为一个一个逗号相隔的参数
@Y-wson

  1. eval和Function都可以做到这件事
    2.不是eval解析这个数组,而是 一元操作符 + 使数组ToString()了吧

from blog.

zhujunwei avatar zhujunwei commented on May 2, 2024

我又来了复习了

from blog.

zaviertang avatar zaviertang commented on May 2, 2024

这样是不是可以避免命名冲突呢?

var x = 'fn'
while (x in context) {
  x = Math.random().toString(16).slice(2, 6)
}
ctx[x] = this
// 调用时使用 ctx[x] 进行调用

或者使用 ES6 的 Symbol 解决:

var x = Symbol()
ctx[x] = this
// 调用时使用 ctx[x] 进行调用

from blog.

gNaW-tuanortsA avatar gNaW-tuanortsA commented on May 2, 2024

执行了args.push('arguments[' + i + ']')后
args = [ 'arguments[1]', 'arguments[2]' ]包含2个字符
然后执行eval('context.fn(' + args + ')')
+令 args 执行 args.toString() 相当于eval('context.fn(' + ‘arguments[1], arguments[2]’+ ')')
字符串合并,即eval('context.fn( arguments[1], arguments[2] )')
执行context.fn( arguments[1], arguments[2] )

from blog.

DangoSky avatar DangoSky commented on May 2, 2024

@mqyqingfeng
第三版的call我觉得还存在几个问题没有考虑到:
· MDN里提到若参数值为原始值时this会指向该原始值的自动包装对象。
· 若是对象本身已经存在了要调用的方法,可能会造成冲突覆盖(使用Symbol)。
· 使用扩展运算符,这样代码可以简洁很多。

这是我改写的模拟call实现,有错的地方请大佬指出。

Function.prototype._call = function(...arg) {
  let context = arg[0] || window; 
  if(typeof context !== 'object') {
    context = Object.create(null); 
  }
  let fn = Symbol();
  context[fn] = this;  
  let result = context[fn](...arg.slice(1));
  delete context[fn];
  return result;   
}

let name = "window";
let obj = {
  name: "local"
}    
function show(param) {
  console.log(this.name);
  console.log(param);
}
show._call(obj, "dangosky");

from blog.

KingJeason avatar KingJeason commented on May 2, 2024

@mqyqingfeng
第三版的call我觉得还存在几个问题没有考虑到:
· MDN里提到若参数值为原始值时this会指向该原始值的自动包装对象。
· 若是对象本身已经存在了要调用的方法,可能会造成冲突覆盖(使用Symbol)。
· 使用扩展运算符,这样代码可以简洁很多。

这是我改写的模拟call实现,有错的地方请大佬指出。

Function.prototype._call = function(...arg) {
  let context = arg[0] || window; 
  if(typeof context !== 'object') {
    context = Object.create(null); 
  }
  let fn = Symbol();
  context[fn] = this;  
  let result = context[fn](...arg.slice(1));
  delete context[fn];
  return result;   
}

let name = "window";
let obj = {
  name: "local"
}    
function show(param) {
  console.log(this.name);
  console.log(param);
}
show._call(obj, "dangosky");

...操作符是es6的

from blog.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.