IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> underscore.js源码中关于对象合并方法的解析(extend,extendOwn,defaults) -> 正文阅读

[JavaScript知识库]underscore.js源码中关于对象合并方法的解析(extend,extendOwn,defaults)

underscore.js源码中关于对象合并方法的解析

源码解析

这篇接着解析underscore.js源码

关于对象合并方法我们首先想到的肯定是Object.assign方法。
在underscore中根据功能不同,分别有extend,extendOwn,defaults三种方法与Object.assign类似,下面通过源码进行解析。

extend,extendOwn,defaults这三个函数其实都是由源码中的createAssigner函数生成的,每个函数的功能略有差异,代码简洁而精妙,非常值得学习。

以下代码的所有汉字部分为我加的注释

createAssigner源码如下:

// 生成不同功能的对象合并函数
function createAssigner(keysFunc, defaults) {
	return function(obj) {
		var length = arguments.length;
		if (defaults) obj = Object(obj);   // 目标对象强转为object类型
		if (length < 2 || obj == null) return obj; // 大于两个参数才进行合并
		for (var index = 1; index < length; index++) { //从第二个参数开始遍历,第一个参数是目标对象
			var source = arguments[index],
				keys = keysFunc(source),      // !这里是关键,不同的keysFunc,生成不同功能的函数
				l = keys.length;
			for (var i = 0; i < l; i++) { //只遍历一层
				var key = keys[i]; // defaults是true时,不覆盖同名属性
				if (!defaults || obj[key] === void 0) obj[key] = source[key];
			}
		}
		return obj;
	};
}

其中最重要的地方就是参数keysFunc,直接看这里肯定会比较懵逼,可以先往下看。

// 合并所有属性,包括源对象上的可枚举继承属性
var extend = createAssigner(allKeys);

// 只合并源对象上的自身属性
var extendOwn = createAssigner(keys);

// 同名属性不会被后面的源对象覆盖,保持目标对象上的值
var defaults = createAssigner(allKeys, true);

以上就是生成三种不同功能函数的方式,大家肯定又懵逼了,allKeys和keys又是个啥。
接着往下看。

// 包括原型链上的可枚举属性
function allKeys(obj) {
	if (!isObject(obj)) return [];
	var keys = [];
	for (var key in obj) keys.push(key); //for in 会遍历原型链上的可枚举属性
	// Ahem, IE < 9.
	if (hasEnumBug) collectNonEnumProps(obj, keys);  //忽略
	return keys;
}

// 只返回自身属性
function keys(obj) {
	if (!isObject(obj)) return [];
	if (nativeKeys) return nativeKeys(obj); //nativeKeys是Object.keys
	var keys = [];
	for (var key in obj)
		if (has(obj, key)) keys.push(key); //has判断是不是自身属性
	// Ahem, IE < 9.
	if (hasEnumBug) collectNonEnumProps(obj, keys);  //忽略
	return keys;
}

allKeys和keys就是返回一个对象上所有键的数组。
区别是allKeys可以返回对象原型链上的可枚举属性。keys方法的效果和Object.keys方法一致,只返回自身属性。

所以extendOwn方法就是只合并源对象上的自身属性,extend方法就是合并源对象的自身属性以及原型链上的可枚举属性。

下面举一个简单的例子来看一下,一个普通对象原型链上的可枚举属性是怎么一回事。

let o1 = {
	a: 1
}
// 不写描述符的情况下,属性默认是可枚举的
let o2 = {
	b: 2
}

Object.setPrototypeOf(o1, o2)   // {a:1}  原型链上有b属性

allKeys(o1) //原型上的可枚举属性也会包括  ['a', 'b']
keys(o1) // ['a']

用Object.setPrototypeOf对o1进行简单的原型链扩展,allKeys(o1)得到的就是a和b两个属性,而keys(o1)则只有a属性。

再回看到createAssigner函数,defaults方法的会合并源对象原型链上的可枚举属性,但是后面的源对象并不会覆盖同名属性,而extend和extendOwn的同名属性则会被最后面的源对象所覆盖。

简介而明了,三个功能不同的函数就这么生成了。

MDN

而原生的Object.assign方法其实就相当于extendOwn方法。

最后再看一下MDN上关于Object.assign方法的polyfill,就会发现它其实跟extendOwn是差不多的,同样都是只合并对象自身属性的第一层,并且会覆盖同名属性。

// mdn上Object.assign的 polyfill  从源码可以看出,Object.assign跟extendOwn的功能基本是一致的
if (typeof Object.assign !== 'function') {
  // Must be writable: true, enumerable: false, configurable: true
  Object.defineProperty(Object, "assign", {
    value: function assign(target, varArgs) { // .length of function is 2
      'use strict';
      if (target === null || target === undefined) {
        throw new TypeError('Cannot convert undefined or null to object');
      }

      var to = Object(target);

      for (var index = 1; index < arguments.length; index++) { //也是从第二个参数开始遍历
        var nextSource = arguments[index];

        if (nextSource !== null && nextSource !== undefined) {
          for (var nextKey in nextSource) {
            // Avoid bugs when hasOwnProperty is shadowed
            if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
              to[nextKey] = nextSource[nextKey];
            }
          }
        }
      }
      return to;
    },
    writable: true,
    configurable: true
  });

用途

如果你想在代码中扩展对象合并的功能,又不想整个引入underscore.js,通过上面的源码分析,就可以只引入你想用到的功能了,如果能自己改写一下那就更厉害了。

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-03-15 22:23:40  更:2022-03-15 22:25:03 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 7:01:08-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码