目录

一、深拷贝和浅拷贝


一、深拷贝和浅拷贝

目前实现深拷贝的方法不多,主要是两种:

  1. 利用 JSON 对象中的 parse 和 stringify
  2. 利用递归来实现每一层都重新创建对象并赋值

1.1 利用 JSON 对象中的 parse 和 stringify

确实是深拷贝,也很方便。但是,这个方法只能适用于一些简单的情况。比如下面这样的一个对象就不适用:

const originObj = {name:'axuebin',sayHello:function(){console.log('Hello World');}
}
console.log(originObj); // {name: "axuebin", sayHello: ƒ}
const cloneObj = JSON.parse(JSON.stringify(originObj));
console.log(cloneObj); // {name: "axuebin"}

发现在 cloneObj 中,有属性丢失了。。。那是为什么呢?

在 MDN 上找到了原因:

If undefined, a function, or a symbol is encountered during conversion it is either omitted (when it is found in an object) or censored to null (when it is found in an array). JSON.stringify can also just return undefined when passing in "pure" values like JSON.stringify(function(){}) or JSON.stringify(undefined).

undefinedfunctionsymbol 会在转换过程中被忽略。。。

明白了吧,就是说如果对象中含有一个函数时(很常见),就不能用这个方法进行深拷贝。

1.2 利用递归来实现每一层都重新创建对象并赋值

1.2.1 简单实现

        function deepClone(source) {let res = {}for (let key in source) {if (Object.prototype.hasOwnProperty.call(source, key)) {// JavaScript中Object对象原型上的hasOwnProperty() 用来判断一个属性是定义在对象本身而不是继承自原型链。// 这里为什么写这么复杂?// javascript没有将hasOwnProperty作为一个敏感词,所以我们很有可能将对象的一个属性命名为hasOwnProperty,这样一来就无法再使用对象原型的 hasOwnProperty 方法来判断属性是否是来自原型链。if (typeof source[key] === 'object') {res[key] = deepClone(source[key])} else {res[key] = source[key]}}}return res}

1.2.2 拷贝数组

        function deepClone(source) {if(!isObject(source)) {return source}let res = Array.isArray(source)? []: {}for (let key in source) {if (Object.prototype.hasOwnProperty.call(source, key)) {// JavaScript中Object对象原型上的hasOwnProperty() 用来判断一个属性是定义在对象本身而不是继承自原型链。// 这里为什么写这么复杂?// javascript没有将hasOwnProperty作为一个敏感词,所以我们很有可能将对象的一个属性命名为hasOwnProperty,这样一来就无法再使用对象原型的 hasOwnProperty 方法来判断属性是否是来自原型链。if (typeof source[key] === 'object') {res[key] = deepClone(source[key])} else {res[key] = source[key]}}}return res}function isObject(source){return typeof source === 'object' && source!=null}

1.2.3 循环引用

我们知道 JSON 无法深拷贝循环引用,遇到这种情况会抛出异常。

1、使用哈希表

解决方案很简单,其实就是循环检测,我们设置一个数组或者哈希表存储已拷贝过的对象,当检测到当前对象已存在于哈希表中时,取出该值并返回即可。

        function deepClone(source, hash = new WeakMap()) {if (!isObject(source)) {return source}console.log(1)if (hash.has(source)) {console.log('source', source)console.log('hash.get(source)', hash.get(source))return hash.get(source)}let res = Array.isArray(source) ? [] : {}hash.set(source, res)for (let key in source) {if (Object.prototype.hasOwnProperty.call(source, key)) {// JavaScript中Object对象原型上的hasOwnProperty() 用来判断一个属性是定义在对象本身而不是继承自原型链。// 这里为什么写这么复杂?// javascript没有将hasOwnProperty作为一个敏感词,所以我们很有可能将对象的一个属性命名为hasOwnProperty,这样一来就无法再使用对象原型的 hasOwnProperty 方法来判断属性是否是来自原型链。if (typeof source[key] === 'object') {res[key] = deepClone(source[key], hash)} else {res[key] = source[key]}}}return res}

2、使用哈希表

// 木易杨
function cloneDeep3(source, uniqueList) {if (!isObject(source)) return source; if (!uniqueList) uniqueList = []; // 新增代码,初始化数组var target = Array.isArray(source) ? [] : {};// ============= 新增代码// 数据已经存在,返回保存的数据var uniqueData = find(uniqueList, source);if (uniqueData) {return uniqueData.target;};// 数据不存在,保存源数据,以及对应的引用uniqueList.push({source: source,target: target});// =============for(var key in source) {if (Object.prototype.hasOwnProperty.call(source, key)) {if (isObject(source[key])) {target[key] = cloneDeep3(source[key], uniqueList); // 新增代码,传入数组} else {target[key] = source[key];}}}return target;
}// 新增方法,用于查找
function find(arr, item) {for(var i = 0; i < arr.length; i++) {if (arr[i].source === item) {return arr[i];}}return null;
}

1.2.4 拷贝 Symbol

这个时候可能要搞事情了,那我们能不能拷贝 Symol 类型呢?

当然可以,不过 Symbol 在 ES6 下才有,我们需要一些方法来检测出 Symble 类型。

方法一:Object.getOwnPropertySymbols(...)

方法二:Reflect.ownKeys(...)

对于方法一可以查找一个给定对象的符号属性时返回一个 ?symbol 类型的数组。注意,每个初始化的对象都是没有自己的 symbol 属性的,因此这个数组可能为空,除非你已经在对象上设置了 symbol 属性。(来自MDN)

function deepClone(source, hash = new WeakMap()) {if (!isObject(source)) {return source}if (hash.has(source)) {console.log('source', source)console.log('hash.get(source)', hash.get(source))return hash.get(source)}let res = Array.isArray(source) ? [] : {}let symkeys = Object.getOwnPropertySymbols(source)// Object.getOwnPropertySymbols() 方法返回一个给定对象自身的所有 Symbol 属性的数组。if( symkeys.length ) {symkeys.forEach(symkey=>{if( isObject(source[symkey]) ){res[symkey] = deepClone(source[symkey], hash)}else {res[symkey] = source[symkey]}})}hash.set(source, res)for (let key in source) {if (Object.prototype.hasOwnProperty.call(source, key)) {// JavaScript中Object对象原型上的hasOwnProperty() 用来判断一个属性是定义在对象本身而不是继承自原型链。// 这里为什么写这么复杂?// javascript没有将hasOwnProperty作为一个敏感词,所以我们很有可能将对象的一个属性命名为hasOwnProperty,这样一来就无法再使用对象原型的 hasOwnProperty 方法来判断属性是否是来自原型链。if (typeof source[key] === 'object') {res[key] = deepClone(source[key], hash)} else {res[key] = source[key]}}}return res}function isObject(source) {return typeof source === 'object' && source != null}// 测试用例var a = {name: "muyiy",book: {title: "You Don't Know JS",price: "45"},a1: undefined,a2: null,a3: 123}// 此处 a 是文章开始的测试用例var sym1 = Symbol("a"); // 创建新的symbol类型var sym2 = Symbol.for("b"); // 从全局的symbol注册?表设置和取得symbola[sym1] = "localSymbol";a[sym2] = "globalSymbol";var b = deepClone(a);console.log(b);

方法二:Reflect.ownKeys(...)

        function deepClone(source, hash = new WeakMap()){if(!isObject){return source}if(hash.has(source)){return hash.get(source)}let res = Array.isArray(source)? [...source]: {...source}hash.set(source, res)console.log('Reflect.ownKeys(res)', Reflect.ownKeys(res))// 静态方法 Reflect.ownKeys() 返回一个由目标对象自身的属性键组成的数组。Reflect.ownKeys(res).forEach(key=>{console.log('key', key)if(isObject(source[key])){res[key] = deepClone(source[key], hash)}else {res[key] = source[key] }})return res}

1.2.5 破解递归爆栈

function deepClone(source) {const root = {}const loopList = [{parent: root,key: undefined,data: source}]while (loopList.length) {// 广度优先const node = loopList.pop()const parent = node.parentconst key = node.keyconst data = node.datalet res = parentif (typeof key !== 'undefined') {res = parent[key] = {}}for(let key in data) {if( data.hasOwnProperty(key) ){if(typeof data[key] === 'object') {loopList.push({parent: res,key,data: data[key]})}else {res[key] = data[key]}}}}return root}

二、Symbol

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Symbol

Symbol.for() - JavaScript | MDN

三、Reflect

js-ES6学习笔记-Reflect - ZhangCui - 博客园

Reflect - JavaScript | MDN

四、setImmediate

Node 定时器详解 - 阮一峰的网络日志

Node 的 Event Loop 分为 6 个阶段:

  • timers:执行setTimeout()setInterval()中到期的callback。
  • pending callback: 上一轮循环中有少数的I/O callback会被延迟到这一轮的这一阶段执行
  • idle, prepare:仅内部使用
  • poll: 最为重要的阶段,执行I/O callback,在适当的条件下会阻塞在这个阶段
  • check: 执行setImmediate的callback
  • close callbacks: 执行close事件的callback,例如socket.on('close'[,fn])http.server.on('close, fn)

五、函数式编程

5.1函数式编程的特点

函数式编程的特点

  • 数据不可变: 它要求你所有的数据都是不可变的,这意味着如果你想修改一个对象,那你应该创建一个新的对象用来修改,而不是修改已有的对象。
  • 无状态: 主要是强调对于一个函数,不管你何时运行,它都应该像第一次运行一样,给定相同的输入,给出相同的输出,完全不依赖外部状态的变化。

其实纯函数的概念很简单就是两点:

  • 不依赖外部状态(无状态): 函数的的运行结果不依赖全局变量,this 指针,IO 操作等。

  • 没有副作用(数据不变): 不修改全局变量,不修改入参。

5.2 函数科里化

在数学和计算机科学中,柯里化是一种将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术。

        var curry = function (fn) {var args = [].slice.call(arguments, 1)console.log('this', this)return function () {var newArgs = args.concat([].slice.call(arguments));console.log('this', this)return fn.apply(this, newArgs)}}function add(a, b) {return a + b}var addCurry = curry(add, 1, 2);console.log('addCurry()', addCurry()) // 3

二、map、set

ES6 新增了 Set 类型,这是一种无重复值的有序列表。 Set 允许对它包含的数据进行快速 访问,从而增加了一个追踪离散值的更有效方式。

创建 Set 并添加项目
Set 使用 new Set() 来创建,而调用 add() 方法就能向 Set 中添加项目,检查 size 属性,还能查看其中包含有多少项:

Set 不会使用强制类型转换来判断值是否重复。这意味着 Set 可以同时包含数值 5 与 字符 串 "5" ,将它们都作为相对独立的项(在 Set 内部的比较使用了第四章讨论过的

Object.is() 方法,来判断两个值是否相等,唯一的例外是 +0 与 -0 在 Set 中被判断为是相 等的)。你还可以向 Set 添加多个对象,它们不会被合并为同一项:

let set = new Set();
set.add(5);
set.add("5");
console.log(set.size)

Set 不会使用强制类型转换来判断值是否重复。这意味着 Set 可以同时包含数值 5 与 字符 串 "5" ,将它们都作为相对独立的项(在 Set 内部的比较使用了第四章讨论过的

Object.is() 方法,来判断两个值是否相等,唯一的例外是 +0 与 -0 在 Set 中被判断为是相 等的)。你还可以向 Set 添加多个对象,它们不会被合并为同一项:

let set = new Set(),key1 = {},
key2 = {};
set.add(key1);
set.add(key2);
console.log(set.size);
// 2

你可以使用 has() 方法来测试某个值是否存在于 Set 中,就像这样:

let set = new Set();
set.add(5);
set.add("5");
console.log(set.has(5));    // true
console.log(set.has(6));    // false

此处的 Set 不包含 6 这个值,因此 set.has(6) 会返回 false 。 移除值

也可以从 Set 中将值移除。你可以使用 delete() 方法来移除单个值,或调用 clear() 方法 来将所有值从 Set 中移除。以下代码展示了二者的作用:

let set = new Set();
set.add(5);
set.add("5");
console.log(set.has(5));
set.delete(5);
console.log(set.has(5));
console.log(set.size);
set.clear();
// true// false // 1console.log(set.has("5"));  // false
console.log(set.size);      // 0

Set 上的 forEach() 方法

forEach() 方法会被传递一个回调函数,该回调接受三个参数:

1. Set中下个位置的值;
2. 与第一个参数相同的值; 3. 目标Set自身。

Set 版本的 forEach() 方法与数组版本有个奇怪差异:前者传给回调函数的第一个与第二个 参数是相同的。虽然看起来像是错误,但这种行为却有个正当理由。

具有 forEach() 方法的其他对象(即数组与 Map )都会给回调函数传递三个参数,前两个 参数都分别是下个位置的值与键(给数组使用的键是数值索引)。

然而 Set 却没有键。 ES6 标准的制定者本可以将 Set 版本的 forEach() 方法的回调函数设 定为只接受两个参数,但这会让它不同于另外两个版本的方法。不过他们找到了一种方式让 这些回调函数保持参数相同:将 Set 中的每一项同时认定为键与值。于是为了让 Set 的forEach() 方法与数组及 Map 版本的保持一致,该回调函数的前两个参数就始终相同了。 除了参数特点的差异外,在 Set 上使用 forEach() 方法与在数组上基本相同。这里有些代码

展示了该方法如何工作:

let set = new Set([1, 2]);
set.forEach(function(value, key, ownerSet) {console.log(key + " " + value);console.log(ownerSet === set);
});

此代码在 Set 的每一项上进行迭代,并对传递给 forEach() 的回调函数的值进行了输出。回 调函数每次执行时, key 与 value 总是相同的,同时 ownerSet 也始终等于 set 。此代 码输出:

11 true 22 true

WeakSet 构造器不接受基本类型的值。

Set 类型之间的关键差异
Weak Set 与正规 Set 之间最大的区别是对象的弱引用。此处有个例子说明了这种差异:

let set = new WeakSet(),key = {};
// 将对象加入
set set.add(key);console.log(set.has(key));      // true
// 移除对于键的最后一个强引用,同时从 Weak Set 中移除
key = null;

当此代码被执行后, Weak Set 中的 key 引用就不能再访问了。核实这一点是不可能的,因 为需要把对于该对象的一个引用传递给 has() 方法(而只要存在其他引用, Weak Set 内部 的弱引用就不会消失)。这会使得难以对 Weak Set 的引用特征进行测试,但 JS 引擎已经正 确地将引用移除了,这一点你可以信任。

这些例子演示了 Weak Set 与正规 Set 的一些共有特征,但是它们还有一些关键的差异,即:

  1. 对于 WeakSet 的实例,若调用 add() 方法时传入了非对象的参数,就会抛出错误( has() 或 delete() 则会在传入了非对象的参数时返回 false );

  2. Weak Set 不可迭代,因此不能被用在 for-of 循环中;

  3. Weak Set 无法暴露出任何迭代器(例如 keys() 与 values() 方法),因此没有任何编

    程手段可用于判断 Weak Set 的内容;

  4. Weak Set 没有 forEach() 方法;

  5. Weak Set 没有 size 属性。

ES6 的 Map

ES6 的 Map 类型是键值对的有序列表,而键和值都可以是任意类型。键的比较使用的Object.is() ,因此你能将 5 与 "5" 同时作为键,因为它们类型不同。这与使用对象属性 作为键的方式(指的是用对象来模拟 Map )截然不同,因为对象的属性会被强制转换为字符串。
你可以调用 set() 方法并给它传递一个键与一个关联的值,来给 Map 添加项;此后使用键名来调用 get() 方法便能提取对应的值。例如:

let map = new Map();
map.set("title", "Understanding ES6");
map.set("year", 2016);
console.log(map.get("title"));      // "Understanding ES6"
console.log(map.get("year"));       // 2016

Map 与 Set 共享了几个方法,这是有意的,允许你使用相似的方式来与 Map 及 Set 进行交互。以下三个方法在 Map 与 Set 上都存在:

has(key) :判断指定的键是否存在于 Map 中;

delete(key) :移除 Map 中的键以及对应的值;

clear() :移除 Map 中所有的键与值。

Map 同样拥有 size 属性,用于指明包含了多少个键值对。以下代码用不同方式使用了这三 种方法以及 size 属性:

let map = new Map();
map.set("name", "Nicholas");
map.set("age", 25);console.log(map.size);//2
console.log(map.has("name"));// true
console.log(map.get("name"));// "Nicholas"
console.log(map.has("age"));// true
console.log(map.get("age")); // 25
map.delete("name");
console.log(map.has("name"));// false
console.log(map.get("name"));// undefined
console.log(map.size); //1
map.clear();
console.log(map.has("name"));// false
console.log(map.get("name"));// undefined
console.log(map.has("age"));// false
console.log(map.get("age"));// undefined
console.log(map.size);//0

Map 上的 forEach 方法
Map 的 forEach() 方法类似于 Set 与数组的同名方法,它接受一个能接收三个参数的回调函

数:

1. Map中下个位置的值; 2. 该值所对应的键;
3. 目标Map自身。

回调函数的这些参数更紧密契合了数组 forEach() 方法的行为,即:第一个参数是值、第二 个参数则是键(数组中的键是数值索引)。此处有个示例:

let map = new Map([ ["name", "Nicholas"], ["age", 25]]);
map.forEach(function(value, key, ownerMap) {console.log(key + " " + value);console.log(ownerMap === map);
});

forEach() 的回调函数输出了传给它的信息。其中 value 与 key 被直接输出, ownerMap 与 map 进行了比较,说明它们是相等的。这就输出了:

name Nicholas
true
age 25
true

Weak Map

Weak Map 对 Map 而言,就像 Weak Set 对 Set 一样: Weak 版本都是存储对象弱引用的方 式。在 Weak Map 中,所有的键都必须是对象(尝试使用非对象的键会抛出错误),而且这 些对象都是弱引用,不会干扰垃圾回收。当 Weak Map 中的键在 Weak Map 之外不存在引用 时,该键值对会被移除。

Weak Map 的最佳用武之地,就是在浏览器中创建一个关联到特定 DOM 元素的对象。例如, 某些用在网页上的 JS 库会维护一个自定义对象,用于引用该库所使用的每一个 DOM 元素, 并且其映射关系会存储在内部的对象缓存中。

该方法的困难之处在于:如何判断一个 DOM 元素已不复存在于网页中,以便该库能移除此元 素的关联对象。若做不到,该库就会继续保持对 DOM 元素的一个无效引用,并造成内存泄 漏。使用 Weak Map 来追踪 DOM 元素,依然允许将自定义对象关联到每个 DOM 元素,而 在此对象所关联的 DOM 元素不复存在时,它就会在 Weak Map 中被自动销毁。

Weak Map 的初始化

为了初始化 Weak Map ,需要把一个由数组构成的数组传递给 WeakMap 构造器。就像正规 Map 构造器那样,每个内部数组都应当有两个项,第一项是作为键的非空的对象,第二项则 是对应的值(任意类型)。例如:

let key1 = {},key2 = {},map = new WeakMap([[key1, "Hello"], [key2, 42]]);
console.log(map.has(key1));
console.log(map.get(key1));
console.log(map.has(key2));
console.log(map.get(key2));
// true
// "Hello"
// true
// 42

对象 key1 与 key2 被用作 Weak Map 的键, get() 与 has() 方法则能访问它们。在传递 给 WeakMap 构造器的参数中,若任意键值对使用了非对象的键,构造器就会抛出错误。

Weak Map 的方法Weak Map 只有两个附加方法能用来与键值对交互。 has() 方法用于判断指定的键是否存在 于 Map 中,而 delete() 方法则用于移除一个特定的键值对。 clear() 方法不存在,这是 因为没必要对键进行枚举,并且枚举 Weak Map 也是不可能的,这与 Weak Set 相同。以下 例子同时用到了 has() 与 delete() 方法:

let map = new WeakMap(),element = document.querySelector(".element");
map.set(element, "Original");
console.log(map.has(element));
console.log(map.get(element));
map.delete(element);
console.log(map.has(element));
console.log(map.get(element));
// true
// "Original"
// false
// undefined

Reflect:提供了统一的API,希望能整合不同数据类型的方法。 与大多数类别对象不同并非Reflect一个构造函数,所以不能通过new运算符进行进行调用,或者将Reflect对象作为一个函数来调用。Reflect的所有属性和方法都是静态的(就像Math对象)。

Reflect.apply(target, thisArgument, argumentsList)

对一个函数进行调用操作,同时可以传入一个数组作为调用参数。和 Function.prototype.apply() 功能类似。

Reflect.construct(target, argumentsList[, newTarget])

对构造函数进行 new 操作,相当于执行 new target(...args)

Reflect.defineProperty(target, propertyKey, attributes)

和 Object.defineProperty() 类似。如果设置成功就会返回 true

Reflect.deleteProperty(target, propertyKey)

作为函数的delete操作符,相当于执行 delete target[name]

Reflect.get(target, propertyKey[, receiver])

获取对象身上某个属性的值,类似于 target[name]。

Reflect.getOwnPropertyDescriptor(target, propertyKey)

类似于 Object.getOwnPropertyDescriptor()。如果对象中存在该属性,则返回对应的属性描述符,  否则返回 undefined.

Reflect.getPrototypeOf(target)

类似于 Object.getPrototypeOf()。

Reflect.has(target, propertyKey)

判断一个对象是否存在某个属性,和 in 运算符 的功能完全相同。

Reflect.isExtensible(target)

类似于 Object.isExtensible().

Reflect.ownKeys(target)

返回一个包含所有自身属性(不包含继承属性)的数组。(类似于 Object.keys(), 但不会受enumerable影响).

Reflect.preventExtensions(target)

类似于 Object.preventExtensions()。返回一个Boolean。

Reflect.set(target, propertyKey, value[, receiver])

将值分配给属性的函数。返回一个Boolean,如果更新成功,则返回true

Reflect.setPrototypeOf(target, prototype)

设置对象原型的函数. 返回一个 Boolean, 如果更新成功,则返回true。

symbol 是一种基本数据类型 (primitive data type)。Symbol()函数会返回symbol类型的值,该类型具有静态属性和静态方法。它的静态属性会暴露几个内建的成员对象;它的静态方法会暴露全局的symbol注册,且类似于内建对象类,但作为构造函数来说它并不完整,因为它不支持语法:"new Symbol()"。每个从Symbol()返回的symbol值都是唯一的。一个symbol值能作为对象属性的标识符;这是该数据类型仅有的目的。

1、可以避免属性名重复 2、创造私有成员

const name = Symbol()
const person = {[name]: 'ren',say() {console.log('name', this[name])}
}
person.say()
console.log(person[name])

Symbol.for(key)

使用给定的key搜索现有的symbol,如果找到则返回该symbol。否则将使用给定的key在全局symbol注册表中创建一个新的symbol。

console.log(Symbol.for(123) === Symbol.for('123')) //自动转化为字符串

Symbol.iterator 为每一个对象定义了默认的迭代器。该迭代器可以被 for...of 循环使用。

let arr = ['hi', ',', 'where', 'are', 'you', 'from']
let arrIter = arr[Symbol.iterator]()
console.log(arrIter.next())
console.log(arrIter.next())
console.log(arrIter.next())
console.log(arrIter.next())

const set = new Set([5,1,2,3,4])
const setIter = set[Symbol.iterator]()
console.log(setIter.next())
console.log(setIter.next())
console.log(setIter.next())
console.log(setIter.next())

const obj = {val: [5, 7, 8, 9, 10, 11],[Symbol.iterator]: function () {let index = 0const self = thisreturn {next: function () {let result = {value: self.val[index],done: index>=self.val.length? true: false}index ++return result}}}
}
// let objIter = obj[Symbol.iterator]()
// console.log(objIter.next())// 利用for of来测试
for(let item of  obj){console.log('item', item)
}

迭代器模式的优点:提供统一迭代接口,屏蔽数据格式差异。

生成器函数逐渐被async、await取代

for of终止遍历,缺点是仅提供值,不提供下标。

Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象分配到目标对象。它将返回目标对象。可以用来复制新对象,设置默认值。

Object.is() 方法判断两个值是否为同一个值。

Object.is() 方法判断两个值是否为同一个值。如果满足以下条件则两个值相等:

  • 都是 undefined
  • 都是 null
  • 都是 true 或 false
  • 都是相同长度的字符串且相同字符按相同顺序排列
  • 都是相同对象(意味着每个对象有同一个引用)
  • 都是数字且
    • 都是 +0
    • 都是 -0
    • 都是 NaN
    • 或都是非零而且非 NaN 且为同一个值

与== 运算不同。  == 运算符在判断相等前对两边的变量(如果它们不是同一类型) 进行强制转换 (这种行为的结果会将 "" == false 判断为 true), 而 Object.is不会强制转换两边的值。

与=== 运算也不相同。 === 运算符 (也包括 == 运算符) 将数字 -0 和 +0 视为相等 ,而将Number.NaN 与NaN视为不相等.

一些JavaScript特性相关推荐

  1. javascript犀牛书_犀牛书作者:最该忘记的JavaScript特性

    作者: 李松峰 转发链接:https://mp.weixin.qq.com/s/guAN1Cz2gYfKdBhmUpLyVA 前言 JavaScript这门语言的第一个演示版差不多就在25年前诞生. ...

  2. 犀牛书作者:最该忘记的JavaScript特性

    编者按:本文译者李松峰,资深技术图书译者,翻译出版过40余部技术及交互设计专著,现任360奇舞团Web前端开发资深专家,360前端技术委员会委员.W3C AC代表. 原文:https://davidf ...

  3. JavaScript 数组拼接打印_犀牛书作者:最该忘记的JavaScript特性

    编者按:本文译者李松峰,资深技术图书译者,翻译出版过40余部技术及交互设计专著,现任360奇舞团Web前端开发资深专家,360前端技术委员会委员.W3C AC代表. 原文:https://davidf ...

  4. 10 个你可能还不知道的很酷JavaScript特性

    英文 | https://javascript.plainenglish.io/5-cool-javascript-features-you-might-not-know-about-yet-f2fc ...

  5. 怎么真正入行Web前端行业?JavaScript五大新特性是什么?

    怎么真正入行Web前端行业?JS五大新特性是什么?JavaScript是Web开发者必学的三种语言之一,是一种具有函数优先的轻量级,解释型或即时编译型的编程语言.随着互联网的更迭以及技术的革新,Jav ...

  6. JavaScript引擎大战:Google提出StrongMode和SoundScript议案,增强V8性能

    JavaScript 引擎性能大战硝烟迷漫,Chrome V8 引擎团队宣布了一个新的计划,在这个计划里他们介绍了两个新的虚拟机概念:强化模式(StrongMode)和强化脚本(StrongScrip ...

  7. 参悟JavaScript

    引子 编程世界里只存在两种基本元素,一个是数据,一个是代码.编程世界就是在数据和代码千丝万缕的纠缠中呈现出无限的生机和活力. 数据天生就是文静的,总想保持自己固有的本色:而代码却天生活泼,总想改变这个 ...

  8. JavaScript引擎V8 5.1遵循了更多的ECMAScript规范并支持WASM

    Google宣布了其5.1版本的V8 JavaScript引擎,这个版本提升了对ECMAScript 2017草案规范的支持,并为WASM提供了初步的支持,WASM是一个低层级的.轻便式的字节码,它致 ...

  9. 悟透 JavaScript

    为什么80%的码农都做不了架构师?>>>    悟透 JavaScript Posted on 2008-02-25 13:32 李战 引子 编程世界里只存在两种基本元素,一个是数据 ...

最新文章

  1. Xilinx 在文档中所用的 LC(logic cells) 与 LUT之间的换算关系
  2. ORA-27301 解决一例
  3. 20200817-Mysql 底层数据结构及Explain详解
  4. urlib2和requests模拟登陆查询MD5
  5. Outh2协议有哪四种授权模式?
  6. python】字符串练习题
  7. 计算机屏幕总闪烁,教你如何解决电脑屏幕闪烁
  8. 【NOIP】OpenJudge - 15:银行利息
  9. Command(命令)
  10. Qt学习之路之启动浏览器
  11. 中国接线端子行业研究与投资前景研究报告(2022版)
  12. 网页实现一个简单的音乐播放器(大佬别看。(⊙﹏⊙))
  13. 手机阅读行业分析(beta)
  14. BeanFacotry中的ApplicationContext
  15. 软件项目管理 6.7.参数估算法
  16. bzoj 4819 [Sdoi2017]新生舞会
  17. 新修复h5盲盒商城砸金蛋源码(完美运行)
  18. 《了凡四训》全文 + 译文
  19. h5跨域访问图片_网页保存为图片及高清截图的优化 | canvas跨域图片配置
  20. linux dhcp认证,如何让linux dhcp server同时支持option 60和option 82认证

热门文章

  1. HK-truedyne液体密度传感器应用案例——大密度介质的测量
  2. Octave Convolution 八度卷积
  3. 淄博职业学院计算机应用技术专业,淄博职业学院计算机应用技术专业2016年在山东理科高考录取最低分数线...
  4. 随记:python-reduce()函数
  5. 计算智能——模糊控制Matlab实现
  6. SQL语句 统计 当日 本周 过去7天 当月 近半年
  7. js 侦听手机摇动事件,模仿微信摇一摇功能
  8. 深度学习常用非线性函数及其导数
  9. Excel基本图形可视化介绍
  10. 30 张Java 的思维导图,全面梳理构建 Java 的知识体系