代码
- 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)
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 可以查看官网. https://developer.mozilla.org/zh-CN/docs/web/JavaScript/Reference/Global_Objects/Object/assign
来源: http://www.qdfuns.com/article/45942/b6edfd870629488722a0ae14d82e8072.html