放肆青春的博客
首页
前端
算法
网络
面试
技术
后端
运维
杂项
数据库
工具
网址
电脑
个人
文章
  • 分类
  • 标签
  • 归档
github (opens new window)
gitee (opens new window)

放肆青春

一个前端菜鸟的技术成长之路
首页
前端
算法
网络
面试
技术
后端
运维
杂项
数据库
工具
网址
电脑
个人
文章
  • 分类
  • 标签
  • 归档
github (opens new window)
gitee (opens new window)
  • 前端

    • 前端 概览
    • 前端汇总

    • front 博文

    • front 项目总结

    • front 高级

    • front tools

  • vue

    • vue 概览
    • vue 汇总

    • vue 博文

    • vue 项目总结

    • vue 高级

  • html

    • html 概览
    • html 汇总

    • html 博文

  • css

    • css 概览
    • css 汇总

    • css 博文

    • sass

    • less

  • js

    • javascript 概览
    • JS 汇总

    • ES6

    • JS 博文

      • js 基础语法
      • js 数据类型
      • js 字符串
      • js 数组
      • js 对象
        • js 对象
          • 对象的循环引用
          • Object.is
          • Object.freeze 和 Object.seal 的区别
          • Object 和 Map 区别
          • 对象的扁平化和反扁平化
          • 判断空对象的几种方法
        • js 对象循环
          • 1. for in
          • 2. Object.keys()
          • 3. Object.getOwnPropertyNames
          • 4. Reflect.ownKeys
          • Object.keys 和 for in 区别
        • 创建对象三种方式
          • 1. {} 字面量
          • 2. new Object()
          • 3. Object.create()
          • 4. 区别
        • js 对象问题汇总
          • for of 怎么遍历对象?
      • js 变量
      • js 函数
      • js 事件
      • js 循环
      • js 浅拷贝和深拷贝
      • js 动画
      • js DOM
      • js 防抖节流
      • js 原型及原型链
      • js this
      • js 作用域
      • js 继承
      • js 闭包
      • js 内存
      • js垃圾回收
    • JS 工具

  • node

    • node 概览
    • node 汇总

    • node 框架

    • node 博文

  • react

    • react 概览
    • react 汇总

    • react 博文

    • react 高级

  • 微信小程序

    • 微信小程序 概览
    • 微信小程序总结
    • 微信小程序文章
    • 微信小程序 博文

    • 微信小程序 高级

  • 微信公众号

    • 微信公众号 概览
    • 微信公众号总结
    • 微信公众号文章
  • 多端开发

    • 多端开发
    • dsbridge 概览
    • jsbridge 概览
    • webview
    • uniapp

      • uniapp 概览
    • taro

      • taro 概览
    • flutter

      • flutter 概览
      • flutter 环境搭建
    • electron

      • electron 概览
  • front
放肆青春
2020-07-09

js 对象

# js 对象

JavaScript 对象是拥有属性和方法的数据。

# 对象的循环引用

循环引用定义为对象的地址和源的地址相同

function circularReference() {
  let obj1 = {};
  let obj2 = {
    b: obj1,
  };
  obj1.a = obj2;
}
1
2
3
4
5
6
7

obj1 中的 a 属性引用 obj2,obj2 中的 b 属性引用 obj1,这样就构成了循环引用

# Object.is

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

Object.is()的用法与全等===基本一致,唯有不同的两点:

  1. +0 与-0 为 false
  2. NaN 与 NaN 为 true
Object.is(0, -0); // false
Object.is(+0, -0); // false
Object.is(0, +0); // true
Object.is(NaN, 0 / 0); // true
1
2
3
4

Object.is() 的实现原理:

Object.is = function(x, y) {
  if (x === y) {
    // 1/+0 = +Infinity, 1/-0 = -Infinity, +Infinity不等于-Infinity
    // Infinity 属性用于存放表示正无穷大的数值。负无穷大是表示负无穷大一个数字值。
    return x !== 0 || 1 / x === 1 / y;
  }
  // 一个变量不等于自身变量,那么它一定是 NaN
  // 两个都是NaN的时候返回true
  return x !== x && y !== y;
};
1
2
3
4
5
6
7
8
9
10

# Object.freeze 和 Object.seal 的区别

Object.freeze()冻结一个对象。不能添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。冻结一个对象后该对象的原型也不能被修改。

Object.seal()封闭一个对象。不能添加新属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,已有属性的值以然可以修改。(也就是说 descriptor 里的 writable 变成没有变,configurable 变成 false 了)

# Object 和 Map 区别

  1. 意外的键

Map 默认情况不包含任何键。只包含显式插入的键。

一个 Object 有一个原型, 原型链上的键名有可能和你自己在对象上的设置的键名产生冲突。

  1. 键的类型

一个 Map 的键可以是任意值,包括函数、对象或任意基本类型。

一个 Object 的键必须是一个 String 或是 Symbol。

  1. 键的顺序

Map 中的 key 是有序的。因此,当迭代的时候,一个 Map 对象以插入的顺序返回键值。

一个 Object 的键是无序的

  1. Size

Map 的键值对个数可以轻易地通过 size 属性获取

Object 的键值对个数只能手动计算

  1. 迭代

Map 是 iterable 的,所以可以直接被迭代。

迭代一个 Object 需要以某种方式获取它的键然后才能迭代。

  1. 性能

Map 在频繁增删键值对的场景下表现更好。

Object 在频繁添加和删除键值对的场景下未作出优化。

使用 Map:

  • 储存的键不是字符串/数字/或者 Symbol 时,选择 Map,因为 Object 并不支持
  • 储存大量的数据时,选择 Map,因为它占用的内存更小
  • 需要进行许多新增/删除元素的操作时,选择 Map,因为速度更快
  • 需要保持插入时的顺序的话,选择 Map,因为 Object 会改变排序
  • 需要迭代/遍历的话,选择 Map,因为它默认是可迭代对象,迭代更为便捷

使用 Object:

  • 只是简单的数据结构时,选择 Object,因为它在数据少的时候占用内存更少,且新建时更为高效
  • 需要用到 JSON 进行文件传输时,选择 Object,因为 JSON 不默认支持 Map
  • 需要对多个键值进行运算时,选择 Object,因为句法更为简洁
  • 需要覆盖原型上的键时,选择 Object

参考:https://zhuanlan.zhihu.com/p/358378689 (opens new window)

# 对象的扁平化和反扁平化

// 实现一个 flatten 函数,实现如下的转换功能
const obj = {
  a: 1,
  b: [1, 2, { c: true }],
  c: { e: 2, f: 3 },
  g: null,
};
// 转换为
let objRes = {
  a: 1,
  "b[0]": 1,
  "b[1]": 2,
  "b[2].c": true,
  "c.e": 2,
  "c.f": 3,
  g: null,
};

Object.flatten = function(obj) {
  var result = {};

  function recurse(src, prop) {
    var toString = Object.prototype.toString;
    if (toString.call(src) == "[object Object]") {
      var isEmpty = true;
      for (var p in src) {
        isEmpty = false;
        recurse(src[p], prop ? prop + "." + p : p);
      }
      if (isEmpty && prop) {
        result[prop] = {};
      }
    } else if (toString.call(src) == "[object Array]") {
      var len = src.length;
      if (len > 0) {
        src.forEach(function(item, index) {
          recurse(item, prop ? prop + ".[" + index + "]" : index);
        });
      } else {
        result[prop] = [];
      }
    } else {
      result[prop] = src;
    }
  }
  recurse(obj, "");

  return result;
};

Object.unflatten1 = function(data) {
  if (Object(data) !== data || Array.isArray(data)) return data;
  var result = {},
    cur,
    prop,
    idx,
    last,
    temp;
  for (var p in data) {
    (cur = result), (prop = ""), (last = 0);
    do {
      idx = p.indexOf(".", last);
      temp = p.substring(last, idx !== -1 ? idx : undefined);
      cur = cur[prop] || (cur[prop] = !isNaN(parseInt(temp)) ? [] : {});
      prop = temp;
      last = idx + 1;
    } while (idx >= 0);
    cur[prop] = data[p];
  }
  return result[""];
};

Object.unflatten2 = function(data) {
  if (Object(data) !== data || Array.isArray(data)) return data;
  var regex = /\.?([^.\[\]]+)|\[(\d+)\]/g,
    resultholder = {};
  for (var p in data) {
    var cur = resultholder,
      prop = "",
      m;
    while ((m = regex.exec(p))) {
      cur = cur[prop] || (cur[prop] = m[2] ? [] : {});
      prop = m[2] || m[1];
    }
    cur[prop] = data[p];
  }
  return resultholder[""] || resultholder;
};
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88

# 判断空对象的几种方法

  1. JSON.stringify
JSON.stringify(data) === "{}"; //true
1
  1. Object.getOwnPropertyNames
var data = {};
var arr = Object.getOwnPropertyNames(data);
arr.length === 0; //true
1
2
3
  1. Object.keys
var data = {};
var arr = Object.keys(data);
arr.length === 0; //true
1
2
3
  1. for in
var obj = {};
var b = function() {
  for (var key in obj) {
    return false;
  }
  return true;
};
alert(b()); //true
1
2
3
4
5
6
7
8

# js 对象循环

# 1. for in

适用范围:对象

for in 概念:以任意顺序遍历一个对象的除 Symbol 以外的可枚举属性,包括继承的可枚举属性。

for-in 语法:for(keys in zhangsan){}

keys 表示 obj 对象的每一个键值对的键!!所有循环中,需要使用 obj[keys]来取到每一个值!!!

for-in 循环,遍历时不仅能读取对象自身上面的成员属性,也能延续原型链遍历出对象的原型属性

所以,可以使用 hasOwnProperty 判断一个属性是不是对象自身上的属性。

obj.hasOwnProperty(keys)==true 表示这个属性是对象的成员属性,而不是原先属性

使用 for in 也可以遍历数组,但是会存在以下问题:

  1. for in 遍历的是数组的索引(即键名),而 for of 遍历的是数组元素值。

  2. 特别情况下, for ... in 循环会以看起来任意的顺序遍历键名

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

  4. index 索引为字符串型数字,不能直接进行几何运算

  5. for in 会遍历手动添加的其他键

var obj = { "0": "a", "1": "b", "2": "c" };

for (var i in obj) {
  console.log(i, ":", obj[i]);
}
1
2
3
4
5

# 2. Object.keys()

返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性).

var obj = { "0": "a", "1": "b", "2": "c" };

Object.keys(obj).forEach(function(key) {
  console.log(key, obj[key]);
});
1
2
3
4
5

# 3. Object.getOwnPropertyNames

返回一个数组,包含对象自身的(不含继承的)所有属性(不含 Symbol 属性,但是包括不可枚举属性)

var obj = { "0": "a", "1": "b", "2": "c" };
Object.getOwnPropertyNames(obj).forEach(function(key) {
  console.log(key, obj[key]);
});
1
2
3
4

# 4. Reflect.ownKeys

返回一个数组,包含对象自身的所有属性,不管属性名是 Symbol 或字符串,也不管是否可枚举.但不包括继承自原型的属性

var obj = { "0": "a", "1": "b", "2": "c" };
Reflect.ownKeys(obj).forEach(function(key) {
  console.log(key, obj[key]);
});
1
2
3
4

# Object.keys 和 for in 区别

  1. Object.keys()不会走原型链,而 for in 会走原型链;

  2. Object.keys()会返回一个数组,而 for in 无返回值;

# 创建对象三种方式

# 1. {} 字面量

# 2. new Object()

new Object()将会根据参数 value 的数据类型,返回对应类型的对象:

  1. 如果 value 为基本数据类型 String、Number、Boolean,则返回对应类型的对象。

  2. 如果 value 本身为对象,则返回其本身。

  3. 如果省略了 value 参数,或 value 为 null、undefined,则返回自身无任何属性的 Object 对象,即返回一个空对象。

var obj = new Object("text");
console.log(obj instanceof Object); //true
console.log(obj instanceof String); //true
console.log(obj.length); //4
console.log(obj[0]); //t
/*即类似于obj = {0:"t", 1:"e", 2:"x", 3:"t"};*/
1
2
3
4
5
6

new 关键字做了几件事

  1. 创建一个新对象

  2. 链接到原型:将新对象的隐式原型指向构造函数的原型

  3. 执行构造函数,绑定 this

  4. 返回这个对象

比如创建一个 Car 对象,伪代码

// new Car()
var obj = new Object();
obj._proto_ = Car.prototype;
// 执行构造函数, 绑定 this
Car.call(obj);
1
2
3
4
5

我们注意到比较关键的地方是,它调用了 Car 对象的构造函数,并通过 call 将 obj 的 this 绑定到了 Car 对象上

这一步操作将 Car 对象上的属性,继承到了 obj 上

手写 new:

  /*
  * 1.创建一个空对象
  * 2.链接到原型
  * 3.绑定this值
  * 4.返回新对象
  */
  function createNew() {
    let obj = {}; // 1.创建一个空对象

    // 把类数组对象转为数组对象,shift() 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值。
    let constructor = [].shift.call(arguments);

    obj.__proto__ = constructor.prototype; // 2.链接到原型

    // 3.绑定this值  使用apply,将构造函数中的this指向新对象,这样新对象就可以访问构造函数中的属性和方法
    let result = constructor.apply(obj, arguments); //

    return typeof result === 'object' ? result : obj; // 4.返回新对象
  }
  function People(name, age) {
    this.name = name;
    this.age = age;
  }
  let peo = createNew(People, 'Bob', 22);
  console.log(peo.name);
  console.log(peo.age);
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

# 3. Object.create()

Object.create()方法接受两个参数: Object.create(obj,propertiesObject) ;

  1. obj:一个对象,应该是新创建的对象的原型。

  2. propertiesObject:可选。该参数对象是一组属性与值,该对象的属性名称将是新创建的对象的属性名称,值是属性描述符(这些属性描述符的结构与 Object.defineProperties()的第二个参数一样)。注意:该参数对象不能是 undefined,另外只有该对象中自身拥有的可枚举的属性才有效,也就是说该对象的原型链上属性是无效的。

创建一个干净的空对象:

Object.create(null) 创建的对象是一个空对象,在该对象上没有继承 Object.prototype 原型链上的属性或者方法,例如:toString(), hasOwnProperty()等方法

手写 create 方法

Object.myCreate(Car) {
  var F = function() {};
  F.prototype = Car;
  var newObj = new F()
  return newObj;
}
1
2
3
4
5
6

newObj.__proto__ == F.prototype == Car

可以看出,在 Object.create 的内部,并没有去调用 Car 构造函数,而是调用了创建新对象的构造函数,因此 Car 上的属性不会继承到 Object.create 创建的实例中

# 4. 区别

  1. {} 字面量和 new Object()区别

new Object()可以传参,new Object()没有传入值和{}是一样的

  1. new Object()和 Object.create()区别

new object(), 原型 Object.prototype

Object.create 可以指定原型,创建的新对象的__proto__指向传入的参数

# js 对象问题汇总

# for of 怎么遍历对象?

  1. 使用 Object.keys
const obj = {
  a: 1,
  b: 2,
  c: 3,
};

for (let i of Object.keys(obj)) {
  console.log(i);
  // 1
  // 2
  // 3
}
1
2
3
4
5
6
7
8
9
10
11
12
  1. 实现 iterator 接口
const obj = {
  e: 5,
  f: 6,
};

newObj[Symbol.iterator] = function*() {
  let keys = Object.keys(this);
  for (let i = 0, l = keys.length; i < l; i++) {
    yield {
      key: keys[i],
      value: this[keys[i]],
    };
  }
};

for (let { key, value } of newObj) {
  console.log(key, value);
}
// 输出结果
// e 5
// f 6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
更新时间: 2/18/2022, 8:04:58 PM
js 数组
js 变量

← js 数组 js 变量→

最近更新
01
前端权限管理
02-24
02
vue2指令
02-24
03
vue2 hook
02-24
更多文章>
Theme by Vdoing | Copyright © 2019-2022 放肆青春
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式