您的位置:奥门新浦京网址 > Wed前段 > 深入之call和apply的模拟实现,深入之new的模拟实

深入之call和apply的模拟实现,深入之new的模拟实

发布时间:2019-10-23 08:38编辑:Wed前段浏览(145)

    JavaScript 深入之call和apply的模拟实现

    2017/05/25 · JavaScript · apply, call

    原文出处: 冴羽   

    10个令人惊叹的HTML5和JavaScript效果

    2011/06/30 · HTML5, JavaScript · 1 评论 · HTML5, Javascript

    作为下一代网页语言,HTML5 拥有很多让人期待已久的新特性。如今,很多 Web 开发人员开始使用 HTML5 来制作各种丰富的 Web 应用。本文向大家展示10个让人眼花缭乱的 HTML5 和 JavaScript 效果,让你体验下一代 Web 技术的魅力,相信你看完这些例子后会对未来的 Web 发展充满无限期待。

    Breathing Galaxies

    奥门新浦京网址 1

    动态变换颜色和直径,可通过键盘或者鼠标产生新的形状,这个效果很炫很炫!

    FlowerPower

    奥门新浦京网址 2

    灵感来自大自然的使用,使用花朵作为画刷,以贝兹曲线方式绘图。

    Noise Field

    奥门新浦京网址 3

    移动鼠标可改变粒子的运动,点击可随机生成不同的粒子效果。

    Keylight

    奥门新浦京网址 4

    双击生成两个以后的键即可发出声音,移动键的位置可产生不同的声音效果。

    Swirling Tentacles

    奥门新浦京网址 5

    三维脉冲效果,沿着脉冲线有运动的颜色渐变模块。

    Blob

    奥门新浦京网址 6

    拖动水滴有重力效果,双击可以分离,小水滴碰到大水滴会合并。

    Rotating Spiral

    奥门新浦京网址 7

    旋转的螺旋效果,看得有点头晕,单击可以控制开始和停止旋转。
    Magnetic

    奥门新浦京网址 8

    模拟磁性粒子流效果,非常逼真。
    Trail

    奥门新浦京网址 9

    彩色颗粒跟随鼠标运动效果,带尾巴淡出效果。
    Graph Layout

    奥门新浦京网址 10

    一种交互的力向图布局效果。

    原文:queness
    译文:梦想天空

     

    赞 2 收藏 1 评论

    奥门新浦京网址 11

    JavaScript 深入之new的模拟实现

    2017/05/26 · JavaScript · new

    原文出处: 冴羽   

    call

    一句话介绍 call:

    call() 方法在使用一个指定的 this 值和若干个指定的参数值的前提下调用某个函数或方法。

    举个例子:

    var foo = { value: 1 }; function bar() { console.log(this.value); } bar.call(foo); // 1

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

    注意两点:

    1. call 改变了 this 的指向,指向到 foo
    2. bar 函数执行了

    new

    一句话介绍 new:

    new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象类型之一

    也许有点难懂,我们在模拟 new 之前,先看看 new 实现了哪些功能。

    举个例子:

    // Otaku 御宅族,简称宅 function Otaku (name, age) { this.name = name; this.age = age; this.habit = 'Games'; } // 因为缺乏锻炼的缘故,身体强度让人担忧 Otaku.prototype.strength = 60; Otaku.prototype.sayYourName = function () { console.log('I am ' + this.name); } var person = new Otaku('Kevin', '18'); console.log(person.name) // Kevin console.log(person.habit) // Games console.log(person.strength) // 60 person.sayYourName(); // I am Kevin

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // Otaku 御宅族,简称宅
    function Otaku (name, age) {
        this.name = name;
        this.age = age;
     
        this.habit = 'Games';
    }
     
    // 因为缺乏锻炼的缘故,身体强度让人担忧
    Otaku.prototype.strength = 60;
     
    Otaku.prototype.sayYourName = function () {
        console.log('I am ' + this.name);
    }
     
    var person = new Otaku('Kevin', '18');
     
    console.log(person.name) // Kevin
    console.log(person.habit) // Games
    console.log(person.strength) // 60
     
    person.sayYourName(); // I am Kevin

    从这个例子中,我们可以看到,实例 person 可以:

    1. 访问到 Otaku 构造函数里的属性
    2. 访问到 Otaku.prototype 中的属性

    接下来,我们可以尝试着模拟一下了。

    因为 new 是关键字,所以无法像 bind 函数一样直接覆盖,所以我们写一个函数,命名为 objectFactory,来模拟 new 的效果。用的时候是这样的:

    function Otaku () { …… } // 使用 new var person = new Otaku(……); // 使用 objectFactory var person = objectFactory(Otaku, ……)

    1
    2
    3
    4
    5
    6
    7
    8
    function Otaku () {
        ……
    }
     
    // 使用 new
    var person = new Otaku(……);
    // 使用 objectFactory
    var person = objectFactory(Otaku, ……)

    模拟实现第一步

    那么我们该怎么模拟实现这两个效果呢?

    试想当调用 call 的时候,把 foo 对象改造成如下:

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

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

    这个时候 this 就指向了 foo,是不是很简单呢?

    但是这样却给 foo 对象本身添加了一个属性,这可不行呐!

    不过也不用担心,我们用 delete 再删除它不就好了~

    所以我们模拟的步骤可以分为:

    1. 将函数设为对象的属性
    2. 执行该函数
    3. 删除该函数

    以上个例子为例,就是:

    // 第一步 foo.fn = bar // 第二步 foo.fn() // 第三步 delete foo.fn

    1
    2
    3
    4
    5
    6
    // 第一步
    foo.fn = bar
    // 第二步
    foo.fn()
    // 第三步
    delete foo.fn

    fn 是对象的属性名,反正最后也要删除它,所以起成什么都无所谓。

    根据这个思路,我们可以尝试着去写第一版的 call2 函数:

    // 第一版 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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // 第一版
    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

    正好可以打印 1 哎!是不是很开心!(~ ̄▽ ̄)~

    初步实现

    分析:

    因为 new 的结果是一个新对象,所以在模拟实现的时候,我们也要建立一个新对象,假设这个对象叫 obj,因为 obj 会具有 Otaku 构造函数里的属性,想想经典继承的例子,我们可以使用 Otaku.apply(obj, arguments)来给 obj 添加新的属性。

    在 JavaScript 深入系列第一篇中,我们便讲了原型与原型链,我们知道实例的 __proto__ 属性会指向构造函数的 prototype,也正是因为建立起这样的关系,实例可以访问原型上的属性。

    现在,我们可以尝试着写第一版了:

    // 第一版代码 function objectFactory() { var obj = new Object(), Constructor = [].shift.call(arguments); obj.__proto__ = Constructor.prototype; Constructor.apply(obj, arguments); return obj; };

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 第一版代码
    function objectFactory() {
     
        var obj = new Object(),
     
        Constructor = [].shift.call(arguments);
     
        obj.__proto__ = Constructor.prototype;
     
        Constructor.apply(obj, arguments);
     
        return obj;
     
    };

    在这一版中,我们:

    1. 用new Object() 的方式新建了一个对象 obj
    2. 取出第一个参数,就是我们要传入的构造函数。此外因为 shift 会修改原数组,所以 arguments 会被去除第一个参数
    3. 将 obj 的原型指向构造函数,这样 obj 就可以访问到构造函数原型中的属性
    4. 使用 apply,改变构造函数 this 的指向到新建的对象,这样 obj 就可以访问到构造函数中的属性
    5. 返回 obj

    更多关于:

    原型与原型链,可以看《JavaScript深入之从原型到原型链》

    apply,可以看《JavaScript深入之call和apply的模拟实现》

    经典继承,可以看《JavaScript深入之继承》

    复制以下的代码,到浏览器中,我们可以做一下测试:

    function Otaku (name, age) { this.name = name; this.age = age; this.habit = 'Games'; } Otaku.prototype.strength = 60; Otaku.prototype.sayYourName = function () { console.log('I am ' + this.name); } function objectFactory() { var obj = new Object(), Constructor = [].shift.call(arguments); obj.__proto__ = Constructor.prototype; Constructor.apply(obj, arguments); return obj; }; var person = objectFactory(Otaku, 'Kevin', '18') console.log(person.name) // Kevin console.log(person.habit) // Games console.log(person.strength) // 60 person.sayYourName(); // I am 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
    26
    27
    28
    function Otaku (name, age) {
        this.name = name;
        this.age = age;
     
        this.habit = 'Games';
    }
     
    Otaku.prototype.strength = 60;
     
    Otaku.prototype.sayYourName = function () {
        console.log('I am ' + this.name);
    }
     
    function objectFactory() {
        var obj = new Object(),
        Constructor = [].shift.call(arguments);
        obj.__proto__ = Constructor.prototype;
        Constructor.apply(obj, arguments);
        return obj;
    };
     
    var person = objectFactory(Otaku, 'Kevin', '18')
     
    console.log(person.name) // Kevin
    console.log(person.habit) // Games
    console.log(person.strength) // 60
     
    person.sayYourName(); // I am Kevin

    []~( ̄▽ ̄)~**

    本文由奥门新浦京网址发布于Wed前段,转载请注明出处:深入之call和apply的模拟实现,深入之new的模拟实

    关键词: