整理总结:关于Javascript---对象篇(含Es6)

一、原始值和对象引用

基本类型: undefined ,null , 布尔值(Boolean) , 字符串(String) , 数值(Number)

  • 赋值时:给的是实实在在的数据值 ,赋值后二者只是值一样,不会相互影响;

    注意:字符串对象的类型是对象,不是字符串。 如下图演示

    //例子1.0.0  字符串赋值
    var a = 100;var b = a;
    b = 200;
    console.log(a)  // 100
    console.log(b)  // 200  赋值后修改互不干预
    
    ----------------------------------------------
    //例子1.0.1  字符串对象比较
    var a = 'foo';var b = 'foo';
    a == b;  // true
    a === b; // true 
    
    var c = new String('foo'); 
    var d = new String('foo'); 
    var e = String('foo'); 
    var f = String('foo');
    
    c == d      // false
    c === d     //false 因为a和b是类型对象,引用不同的对象
    c == 'foo'  // true  因为在比较前 a.toString()转换成了 字符串 此时就是字符串和字符串比较
    
    e === a    //true
    e === f    //true
    c == f     //false
    c === f    //false
    
    # 不使用new时返回的是一个基本类型字符串,效果与使用'hello'字面量创建是一样的
    # 使用new 用构造函数返回字符串本身的类型是对象,此时同样需要: 当且仅当它们引用同一个基对象时,它们才相等

引用类型: 统称为Object类型,细分的话,有:Object类型,Array类型,Function类型等。

  • 赋值时:当且仅当它们引用同一个基对象时,它们才相等。 也就是仅仅赋值了引用值,所以新旧数据会互相影响,因为本质上还是同一个数据对象;
  • 想得到一个副本,遍历赋值对象的每个属性或元素

    //例子1.0.2  引用类型赋值
    var a = {"age":20};var b = a; 
    b.age = 21;
    console.log(a.age)   //21
    console.log(b.age)   //21
    
    var c = ["1","3","5"];var d = c;
    d.push("6");
    console.log(c)       // ["1", "3", "5", "6"]
    console.log(d)       // ["1", "3", "5", "6”]

二、对象常见的用法:创建 设置 查找 删除 检测 枚举

1. 创建对象的方法:

(1)对象字面量的方式
var newObj1 = {x:1,y:2}; 
(2)使用new关键字,如:new Object()
var newObj2 = new Object();
newObj2.x = 1;
newObj2.printKey = function(){
    return this.x;
};
(3)构造函数
function Person(num){
    this.x = num;
    this.printKey = function(){
        return this.x;
    }
}
var newObj3 = new Person(1);
(4)工厂方式创建
function Factory(num){
    var obj = new Object();
    obj.x = num;
    obj.printKey = function(){
        return obj.x;
    }
    return obj;
}
var newObj4 = new Factory(1);
(5)原型方式创建
function Person(){}
Person.prototype.name = "lisi";
Person.prototype.say = function(){
     alert(this.name);
};
var newObj5 = new Person();
(6)Object.create()函数来创建

创建一个对象并指定对象原型
语法:Object.create(proto[, propertiesObject]);
参数一:指定对象的原型对象。
参数二:对象的属性描述符;没有默认undefined 添加到新创建对象的不可枚举

value: 属性的默认值, 默认为undefined
congigurable: 能否使用delete、能否修改属性特性、或能否修改访问器属性、false为不可重新定义,默认值为true
enumberable: 可枚举性,对象属性能否通过for-in 循环,默认为true
writable: 对象属性是否可修改,默认为true,可修改,设置false可理解为常量不可修改
var o1=Object.create({x:1,y:2});
//创建了一个原型对象为{x: 1, y: 2}的对象o1; 打印o1.__proto__ 》》{x: 1, y: 2}

var o2=Object.create(null);
//创建一个原型为null的空对象;o2不继承任何属性和方法;

var o3 = Object.create(Object.prototype);
//o3和{}和new Object()一样;

var o4  = Object.create(Object.prototype, {
  // foo会成为所创建对象的数据属性
  foo: { 
    writable:true,
    configurable:true,
    value: "hello" 
  },
  // bar会成为所创建对象的访问器属性
  bar: {
    configurable: false,
    get: function() { return 10 },
    set: function(value) {
      console.log("Setting `o.bar` to", value);
    }
  }
});

----------------------------------------------

// 创建一个以另一个空对象为原型,且拥有一个属性p的对象
var o = Object.create({}, { p: { value: 42 } })

o.p = 24; //设置p的值
o.p; //42 依然是42 。因为:省略了的属性特性默认为false,所以属性p是不可写,不可枚举,不可配置的    



## 基于属性描述符的补充
    # Object.getOwnpropertyDescriptor() 
    //获得对象自身属性的属性描述符
    
    # Object.getPrototypeOf() 
    //查询原型
    
    # Object.defineProperty() 
    //修改或创建单个属性的描述符
    
    # Object.defineProperties() 
    //修改或创建多个个属性的描述符

Object.getOwnPropertyDescriptor(o4,"foo");//{value: "hello", writable: true, enumerable: false, configurable: true}
Object.getOwnPropertyDescriptor(o4,"toString");//undefined; 继承属性非自身属性;

----------------------------------------------
var o5={a:9};
Object.defineProperty(o5,"x",{value:1,writable: true, enumerable: false, configurable: true});//添加一个不可枚举的数据属性x,并赋值为1;
o5.x;//1;
Object.keys(o5);//可枚举数组为["a"] ;x不可枚举

Object.defineProperty(o5,"x",{writable:false});//x变成只读;
o5.x = 2;//试图修改x的值
o5.x;//1 依然是1;
Object.defineProperty(o5,"x",{value:2});//能修改

Object.defineProperty(o5,"b",{get:function(){
    return this.a;
}})//新增b为存取属性
o5.b //9;

2. 设置查找:

data.aaa 可写成 data[aaa] 前面认定是字符串,后者可以是表达式 ; 可读取可设值。

var data = {key1:"peo",key2:"12"};
console.log(data.key1);     //peo
console.log(data['key'+1]); //peo

data.key1 = 'jerry';       //{key1: "jerry", key2: "12"}
data['key'+2] = 'student'; //{key1: "jerry", key2: "student"}

3. 删除delete:

只能删除自有属性 不能删除继承属性(要删除继承属性必须从定义这个属性的原型对象上删除它,而且这会影响多有继承自这个原型的对象)

var data = {key1:"peo",key2:"12"};
delete data.key2; //返回true 且 data》{key1:"peo"}
delete data.name; //返回true 要删除的本身就不存在 

4. 检测属性:

in运算符、hasOwnPreperty()、propertyEnumerable()

//in运算符 >> 检测自身或继承中是否有该属性
var data = {x:1};
"x" in data; //true;
"y" in data; //false;
"toString" in data; //true: data继承toString属性

//hasOwnProperty() >> 检测自身属性中是否有该属性 
data.hasOwnProperty("x"); //true
data.hasOwnProperty("y"); //false
data.hasOwnProperty("toString"); //false :只检测自身属性,toString是继承来的属性

//propertyIsEnumerable() >> 检测自身属性中是否有该属性且该属性是可枚举的  

5.枚举(enumerate):

for/in循环 遍历对象中所有可枚举的属性(包含自身属性和继承的属性)

propertyIsEnumerable() 检测属性可否枚举

var o = {x:1,y:2,z:3}; //可枚举的自有属性
o.propertyEnumerable("toString");//false,不可枚举
for(p in o){
    console.log(p);//输出x,y,z;不输出toString
}

6. 属性的getter和setter

对象由一组名字、值组成。值可以是普通数据属性,也可以是个方法;又getter和setter定义的属性称作“存取器属性”;
如果同时具有getter和setter方法,那么它是个读/写属性;
如果只有getter方法,它是个只读属性;
如果只有setter方法,它是个只写属性,读取只写属性总返回undefined

var p = {
    x:1.0,
    y:1.2, //x、y是普通可读写的数据属性
    
    //r是可读写的存取器属性,它有getter和setter ;
    //另外注意:这个函数定义没有使用function关键字,而是使用了get或set。
    get r(){return Math.sqrt(this.x*this.x+this.y*this.y);},
    set r(newValue){
        var oldValue = Math.sqrt(this.x*this.x+this.y*this.y);
        var ratio = newValue/oldValue;
        this.x *= ratio;
        this.y *= ratio;
    },
    
    //theta是只读的存取器属性,只有getter方法
    get theta(){return Math.atan2(this.y,this.x);}
}

三、对象的三个属性:原型、类、可拓展性

每个对象都有原型prototype、类class、可拓展性extensible attrbute

原型属性:对象的原型属性是用来继承的;

Object.getPrototypeOf();可以查询它的原型;

检测一个对象是否是另一个对象的原型(isPrototypeOf 和instanceof非常类似)
A.isPrototypeOf(B) //A对象是否在B的原型链上
A instanceof B // A是不是B的实例;B构造函数的prototype对象是否在A的原型链上

var p = {x:1};
var o = Object.create(p);//创建一个原型指向p的对象o;
p.isPrototypeOf(o); //true:p对象在o的原型链上
Object.prototype.isPrototypeOf(o); //true:p继承自Object.prototype;

-----------------------
var d = new Date();//通过Date()构造函数创建一个对象
d instanceof Date; //true;d是由Date()创建的
d instanceof Object;//true;所有的对象都是Object的实例
var a = [1,2,3];
a instanceof Array;//true;
a instanceof Object;//true;

function Foo(name, age){this.name = name;}
var f = new Foo('jerry');
f instanceof Foo  //true  f属于Foo这个构造函数的实例化
f instanceof Object//true  所有的对象都是Object的实例

类属性:对象的类属性是一个字符串,用以表示对象的类型信息;可调用对象的toString()方法查询

function classof(o){
    if(o === null) return "Null";
    if(o === undefined) return "Undefined";
    return Object.prototype.toString.call(o).slice(8,-1)
}
classof([])//"Array"
classof({})//"Object"
classof(function(){}) //"Function"
function f(){}; classof(new f())//"Object"
classof(/\d/)//"Regexp"
classof(window)//"Window"这个客户端宿主对象

可扩展性:是否可以给对象添加新属性

  • Object.isExtensible() //判断该对象是否可扩展。
  • Object.preventExtensions() //阻止向对象添加新的属性;将对象转为不可扩展的,一旦转为不可扩展 无法转回可扩展
  • Object.seal() //阻止向指定对象添加新属性或者删除现有属性;已有的可写属性依然可以设置。
  • Object.freeze() //将指定对象设置为不可改变;更严格的锁定,
  • Object.isSealed() //检查当前对象是否为封闭;
  • Object.isFrozen() //检查当前对象是否为冻结;

序列化对象:

  • JSON.stringify() //把对象转字符串
  • JSON.parse() //json字符串转json

四、Object 原生方法

分成两类: 原型方法和 静态函数

  • Object 原型方法定义在 Object.prototype 对象上,所有 Object 实例对象都会继承这些方法。
  • Object 静态函数就是定义在 Object 对象上的方法,通过 Object 直接调用,不需要实例继承。

原型方法:

方法 描述
hasOwnProperty() 检测自身属性中是否有该属性
isPrototypeOf() 检测当前对象是不是指定对象的原型
propertyIsEnumerable() 检测自身属性中是否有该属性且该属性是可枚举的
toString() 返回该对象的字符串表示
toLocaleString() 返回该对象的一个本地化的字符串表示
valueOf() 返回该对象的原始值
Object.prototype.toString.call(x).slice(8,-1);
//调用这个方法 截取字符串的第8位到倒数第2位 是读取它的属性

----------------------

const num = 2333333;
num.toLocaleString();   // 2,333,333
num.toLocaleString('zh', { style: 'currency', currency: 'CNY' });    //¥2,333,333.00
num.toLocaleString('zh', { style: 'currency', currency: 'cny', currencyDisplay: 'code' });      //CNY2,333,333.00
num.toLocaleString('zh', { style: 'currency', currency: 'cny', currencyDisplay: 'name' });      //2,333,333.00人民币

静态方法:

方法 说明
对象原型
Object.create() 使用指定原型及属性创建一个新的对象
Object.getPrototypeOf() 查询原型获取对象的 Prototype 对象
遍历对象
Object.keys() 以数组形式返回参数对象包含的可枚举的私有属性的key名;
Object.values() 以数组形式返回参数对象包含的可枚举的私有属性的value
Object.getOwnPropertyNames 以数组形式返回参数对象包含的私有属性名;
对象属性
Object.defineProperty() 创建或配置指定对象的某个属性
Object.defineProperties() 创建或配置指定对象的一个或多个属性
Object.getOwnpropertyDescriptor() 获得对象自身属性的属性描述符
对象状态控制
Object.preventExtensions() 阻止向对象添加新的属性;将对象转为不可扩展的,一旦转为不可扩展 无法转回可扩展
Object.isExtensible() 判断该对象是否可扩展。
Object.seal() 阻止向指定对象添加新属性或者删除现有属性;已有的可写属性依然可以设置。
Object.freeze() 将指定对象设置为不可改变;冻结一个对象。更严格的锁定。
Object.isSealed() 检查当前对象是否为封闭;
Object.isFrozen() 检查当前对象是否为冻结;

五、Es6-对象的扩展

1.简洁表示法

function f(x, y) {
  return {
    // 等同于hello: function ()...
    hello() { console.log('我的名字是', this.name); },
    
    //等同于 x: x, y: y
    x, y
  }; 
}

2.属性名表达式

Es5在{}里面key值只能是字面量,
Es6可以在{}里面用[]写表达式。

let propKey = 'foo';
var obj = {
  //Es5只支持字面量
  foo: true, 
  
  //用[]写表达式
  [propKey]: true,
  ['a' + 'bc']: 123,
  ['h' + 'ello']() {
    return 'hi';
  }
};

3.方法的 name 属性

  • 普通函数,name属性返回:函数名
  • getter|setter函数,name属性返回:get|set 加 函数名
  • bind方法创造的函数,name属性返回:bound 加 原函数名
  • Function构造函数创造的函数,name属性返回:anonymous
  • 对象的方法是Symbol值,name属性返回:Symbol值的描述

4.属性的可枚举性和遍历

可枚举性

目前,有四个操作会忽略enumerable为false的属性。

for...in循环:只遍历对象自身的和继承的可枚举的属性。
Object.keys():返回对象自身的所有可枚举的属性的键名。
JSON.stringify():只串行化对象自身的可枚举的属性。
Object.assign(): 忽略enumerable为false的属性,只拷贝对象自身的可枚举的属性。

属性的遍历

ES6 一共有 5 种方法可以遍历对象的属性。

(1) for...in
遍历对象自身的和继承的可枚举属性(不含 Symbol 属性)。

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

(3) Object.getOwnPropertyNames(obj)
返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)的键名。

(4) Object.getOwnPropertySymbols(obj)
返回一个数组,包含对象自身的所有 Symbol 属性的键名。

(5) Reflect.ownKeys(obj)
返回一个数组,包含对象自身的所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举。


以上的 5 种方法遍历对象的键名,都遵守同样的属性遍历的次序规则。
首先遍历所有数值键,按照数值升序排列。
其次遍历所有字符串键,按照加入时间升序排列。
最后遍历所有 Symbol 键,按照加入时间升序排列。

5.super 关键字

作用: 用于访问父对象上的函数。
详解:super可以用在类的继承中,或者对象字面量中,super指代了整个prototype或者__proto__指向的对象。
语法:

super([arguments]); // 访问父对象上的构造函数

super.functionOnParent([arguments]); // 访问对象上的方法

6.对象的扩展运算符: ...

7.链判断运算符: ?.

//Es5
const firstName = (message
  && message.body
  && message.body.user
  && message.body.user.firstName) || 'default';
  
//Es6
const firstName = message?.body?.user?.firstName || 'default';
const fooValue = myForm.querySelector('input[name=foo]')?.value

8.Null 判断运算符: ??

只有运算符左侧的值为nullundefined时,才会返回右侧的值

const animationDuration = response.settings.animationDuration ?? 300;

六、Es6-对象的新增方法

1.Object.is()

严格等于,不会做隐式类型转换, 其行为与===基本一致,不过有两处不同:

  1. +0不等于-0**
  2. NaN等于自身

2.Object.assign()

基础:用于对象的合并,将源对象source的所有可枚举属性,复制到目标对象target
注意:
(1) 忽略enumerable为false的属性,只拷贝对象自身的可枚举的属性。
(2) 属于浅拷贝
常用于:

## 合并 替换值
const target = { a: 1, b: 1 };
const source1 = { b: 2, c: 2 };
const source2 = { c: 3 };

Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}

-----------------

## 如果只有一个参数,`Object.assign`会直接返回该参数
typeof Object.assign(2) // "object" 

## 由于`undefined`和`null`无法转成对象,所以如果它们作为参数,就会报错。
Object.assign(undefined) // 报错
Object.assign(null) // 报错

3.Object.getOwnPropertyDescriptors()

主要是为了解决Object.assign()无法正确拷贝get属性和set属性的问题。

ES5 的Object.getOwnPropertyDescriptor()方法会返回某个对象属性的描述对象;
ES2017 引入了Object.getOwnPropertyDescriptors()方法,返回指定对象所有自身属性(非继承属性)的描述对象

该方法的实现:

function getOwnPropertyDescriptors(obj) {
  const result = {};
  for (let key of Reflect.ownKeys(obj)) {
    result[key] = Object.getOwnPropertyDescriptor(obj, key);
  }
  return result;
}

4.Object.setPrototypeOf(),Object.getPrototypeOf()

__proto__本质上是一个内部属性,所以从语义的角度,还是从兼容性的角度,都不要使用这个属性,而是使用下面操作代替:
Object.setPrototypeOf()(写操作)
Object.getPrototypeOf()(读操作)
Object.create()(生成操作)

Object.setPrototypeOf(object, prototype) 写入
// 用法
const o = Object.setPrototypeOf({}, null);

//等同于
function setPrototypeOf(obj, proto) {
  obj.__proto__ = proto;
  return obj;
}
Object.getPrototypeOf(obj) 读取

如果参数不是对象,会被自动转为对象

5.Object.keys(),Object.values(),Object.entries()

ES2017引入了跟Object.keys配套的Object.valuesObject.entries,作为遍历一个对象的补充手段,供for...of循环使用

var obj = {name:"tom",age:12,sex:"man"};

Object.keys(obj);//["name", "age", "sex"] 获取对象的key组合数组
for (let key of keys(obj)) {
  console.log(key); // "name", "age", "sex"
}

Object.values(obj)//["tom", 12, "man"] 获取对象的value组合数组
for (let value of values(obj)) {
  console.log(value); // "tom", 12, "man"
}

Object.entries(obj)// 
[["name", "tom"],["age", 12], ["sex", "man"]]
for (let [key, value] of entries(obj)) {
  console.log([key, value]); // ["name", "tom"],["age", 12], ["sex", "man"]
}

6. Object.fromEntries()

Object.fromEntries()方法是Object.entries()的逆操作,用于将一个键值对数组转为对象。
主要目的: 是将键值对的数据结构还原为对象,因此特别适合将 Map 结构转为对象。

// 例一
const entries = new Map([
  ['foo', 'bar'],
  ['baz', 42]
]);

Object.fromEntries(entries)
// { foo: "bar", baz: 42 }

// 例二
const map = new Map().set('foo', true).set('bar', false);
Object.fromEntries(map)
// { foo: true, bar: false }

mark一下 仅供参考 欢迎更正补充 end


参考资料:

阅读 350

推荐阅读
喈喱前端笔记
用户专栏

学习的付出 从不欺人

4 人关注
30 篇文章
专栏主页