原生javascript实现extend

2018-04-15 admin

代码

var obj1 = {'a': 'obj2','b':'2'};
var obj2 = {name: 'obj3'};
function extend() {
    var length = arguments.length;
    var target = arguments[0] || {};
    if (typeof target!="object" && typeof target != "function") {
        target = {};
    }
    if (length == 1) {
        target = this;
        i--;
    }
    for (var i = 1; i < length; i++) { 
        var source = arguments[i]; 
        for (var key in source) { 
            // 使用for in会遍历数组所有的可枚举属性,包括原型。
            if (Object.prototype.hasOwnProperty.call(source, key)) { 
                target[key] = source[key]; 
            } 
        } 
    }
    return target; 
}
console.log(extend(obj1,obj2));

extend 要实现的是给任意对象扩展

分析一下

在extend()函数中没有写死参数,是为了更好的扩展性,永远也不知道需要扩展的对象有几个。 而是通过arguments来获取传进来的参数。

arguments对象不是一个 Array 。它类似于Array,但除了length属性和索引元素之外没有任何Array属性。

// 可以转换为数组 ES2015
const args = Array.from(arguments);

console.log(typeof arguments); // 'object'

target

target是传进来的第一个参数,也就是需要扩展的对象。

var target = arguments[0] || {}; // 如果没有传参,则设为一个空对象

// 进行这一步判断是为了保证代码的可执行性,如果传进来的是个数字、布尔值,则设为一个空对象
if (typeof target!="object" && typeof target != "function") {
    target = {};
}

循环遍历赋值

for (var i = 1; i < length; i++) { 
    var source = arguments[i]; 
    for (var key in source) { 
        // 使用for in会遍历数组所有的可枚举属性,包括原型。
        if (Object.prototype.hasOwnProperty.call(source, key)) { 
            target[key] = source[key]; 
        } 
    } 
}

这一步就是将扩展源里的属性、方法循环遍历赋值到扩展项中。

如果扩展项和扩展源中有相同的属性、方法,后面的会覆盖前面的。 这个思想也是插件开发中,实现用户配置覆盖默认设置的实现思想。

hasOwnProperty

为什么需要使用hasOwnProperty,这跟for in有密切关系。

使用for in会遍历所有的可枚举属性,包括原型。

所以需要判断一下,是否是对象自身的属性,而不是继承于原型的。

那为什么不直接使用source.hasOwnProperty(source[key])呢?

JavaScript 并没有保护 hasOwnProperty 属性名,因此某个对象是有可能存在使用这个属性名的属性,使用外部的 hasOwnProperty 获得正确的结果是需要的:

var foo = {
    hasOwnProperty: function() {
        return false;
    },
    bar: 'Here be dragons'
};

foo.hasOwnProperty('bar'); // 始终返回 false

// 如果担心这种情况,可以直接使用原型链上真正的 hasOwnProperty 方法
({}).hasOwnProperty.call(foo, 'bar'); // true

// 也可以使用 Object 原型上的 hasOwnProperty 属性
Object.prototype.hasOwnProperty.call(foo, 'bar'); // true

call apply

上面用到的call和apply,就在这里记录一下。

1.每个函数都包含两个非继承而来的方法:call()方法和apply()方法。 2.相同点:这两个方法的作用是一样的。 都是在特定的作用域中调用函数,等于设置函数体内this对象的值,以扩充函数赖以运行的作用域。 一般来说,this总是指向调用某个方法的对象,但是使用call()和apply()方法时,就会改变this的指向。 3.不同点:接收参数的方式不同。

  • apply()方法 接收两个参数,一个是函数运行的作用域(this),另一个是参数数组。

语法:apply([thisObj [,argArray] ]);,调用一个对象的一个方法,2另一个对象替换当前对象。 说明:如果argArray不是一个有效数组或不是arguments对象,那么将导致一个TypeError,如果没有提供argArray和thisObj任何一个参数,那么Global对象将用作thisObj。

  • call()方法 第一个参数和apply()方法的一样,但是传递给函数的参数必须列举出来。

语法:call([thisObject[,arg1 [,arg2 [,…,argn]]]]);,应用某一对象的一个方法,用另一个对象替换当前对象。 说明: call方法可以用来代替另一个对象调用一个方法,call方法可以将一个函数的对象上下文从初始的上下文改变为thisObj指定的新对象,如果没有提供thisObj参数,那么Global对象被用于thisObj。

// call
    window.name = 'FinGet';
    document.name = 'FinGet1';

    var boy = {name: 'FinGet2' };
    function showName(){
        console.log(this.name);
    }

    showName.call();         //FinGet (默认传递参数)  this 是指向window
    showName.call(window);   //FinGet
    showName.call(document); //FinGet1
    showName.call(this);     //FinGet
    showName.call(boy);       //FinGet2

    var Pet = {
        words : 'hello',
        speak : function (say) {
            console.log(say + ''+ this.words)
        }
    }
    Pet.speak('Speak'); // 结果:Speakhello

    var Dog = {
        words:'Wang'
    }

    //将this的指向改变成了Dog
    Pet.speak.call(Dog, 'Speak'); //结果: SpeakWang

可以将上面代码中的call换成apply,也是可以执行的。

Object.assign

Object.assign(target, …sources)

如果目标对象中的属性具有相同的键,则属性将被源中的属性覆盖。后来的源的属性将类似地覆盖早先的属性。 注意,Object.assign 会跳过那些值为 null 或 undefined 的源对象。

var obj1 = {a:'1',b:'2'};
var obj2 = {c:'3',d:'4'};
Object.assign(obj1,obj2); // Object {a: "1", b: "2", c: "3", d: "4"}

obj1 也改变
Object {a: "1", b: "2", c: "3", d: "4"}

obj2
Object {c: "3", d: "4"}

更多相关Object.assign可以查看官网。

相关文章
Vue.js组件tab实现选项卡切换
本文实例为大家分享了vue插件tab选项卡的具体代码,供大家参考,具体内容如下 效果图: 代码如下: &lt;!DOCTYPE html&gt; &lt;html lang=&quot;en&quot;&gt; &lt;head&gt; ...
2017-03-13
JavaScript教程:JS中的原型
Keith Peters 几年前发表的一篇博文,关于学习没有“new”的世界,其中解释了使用原型继承代替构造函数。两者都是纯粹的原型编码。 标准方法(The Standard Way) 一直以来,我们学习的在 JavaScript 里创建对...
2015-11-12
javascript是什么意思
avaScript是Netscape开发的一个对象脚本语言,它使用在世界各地数以百万计的网页和服务器应用程序上。 网景的JavaScript是ecma - 262版的标准脚本语言,和公布的标准只有轻微的差异。 与广为流行的错误理解相反,Ja...
2015-11-12
21天学通javascript
简介: 本书是Javascript入门教程。Javascript是Web开发中应用最早、发展最成熟、用户最多的脚本语言。其语法简洁,代码可读性在众多脚本语言中最好,它在使用时不用考虑数据类型,是真正意义上的动态语言。本书总分为四篇,共21章...
2015-11-16
three.js实现围绕某物体旋转
话不多说,请看代码: 可以拖动右上角观察变化 &lt;!DOCTYPE html&gt; &lt;html lang=&quot;en&quot; style=&quot;width: 100%; height:100%;&quot;&gt...
2017-02-17
JavaScript的组成
一个完整的JavaScript由3个部分组成:核心(ECMAScript) 文档对象模型(DOM) 浏览器对象模型(BOM) ECMAScript 描述了该语言的语法和基本对象 ; DOM 描述了处理网页内容的方法和接口 ; BOM 描...
2015-11-12
JavaScript 事件流、事件处理程序及事件对象总结
JS与HTML之间的交互通过事件实现。事件就是文档或浏览器窗口中发生的一些特定的交互瞬间。可以使用监听器(或处理程序)来预定事件,以便事件发生时执行相应的代码。这种在传统软件工程中被称为观察员模式,支持页面的行为与页面的外观之间的松散耦合。...
2017-04-05
JavaScript变量的声明
声明变量 变量在脚本中的首次亮相是在其声明中。 在变量首次出现时将会在内存中设置它,因此您稍后可在脚本中引用它。 应在使用变量之前先声明变量。 可以使用 var 关键字实现此目的。 &lt;span id=“mt9” class=“sent...
2015-11-12
7个提高效率的JavaScript调试工具
鐜板湪鐨凧avaScript浜嬪疄涓婂凡鐒舵垚涓轰簡娴佽�岀殑web璇�瑷€锛屽嵆浣垮畠骞朵笉瀹岀編銆傚緢澶氱▼搴忓憳涓嶅枩娆㈢敤JavaScript鍐欎唬鐮侊紝鏄�鍥犱负鍐欏埌鍚庢潵鎬讳細鍑虹幇鍚勭�嶈帿鍚嶅叾濡欑殑bug锛岃€屼笖鍦ㄥ紑...
2015-11-11
7个让JavaScript变得更好的注意事项
随着浏览器性能提高,伴随着新的HTML5的编程接口的稳步采用,网页上的JavaScript的音量在逐渐增加。然而,一个写得不好的程序编码却拥有着打破整个网站,让用户为之沮丧和驱赶潜在客户的潜力。 开发人员必须使用所有供他们任意使用的工具和技...
2015-11-12
回到顶部