介绍

除了 字符串,数字,true,false, null 和 undefined,都是对象
js 的对象是关联数组
obj.pro 像对象
obj[‘pro’] 像数组了

对象包含属性,三个对象特性

  • prototype
  • class 使用 Object.prototype.toString.call()
  • extensible flag

以对象的基本内置方法划分,总共有4种对象

  • 宿主对象
  • 内置对象

    • 固有对象
    • 原生对象
    • 普通对象:由{}语法、Object构造器或者class关键字定义类创建的对象,它能够被原型继承。
  • ordinary object(普通对象):由{}语法、Object构造器或者class关键字定义类创建的对象,它能够被原型继承。

  • exotic object(外来对象):如果不具备标准对象所有的基本内置方法,就是外来对象。JS里的对象不是普通对象就是外来对象。
  • standard object(标准对象): ecma 规定的对象
  • build-in object(内建对象):javascript 引擎实现的对象,包括宿主对象

解释

  • 一个对象符合ECMAScript语义规范,它就可以称为标准对象。
  • ECMAScript实现 中除了ECMAScript规范中规定的内置对象,还提供了与宿主环境相关的内置对象,例如,浏览器环境存在location、document对象,这些也称为内置对象。
  • 标准内置对象 则特指ECMAScript规范中所定义的内置对象。

属性有根据是否是自有的,有两种分类

  • 自有属性
  • 继承属性

内置对象·固有对象

固有对象是由标准规定,随着JavaScript运行时创建而自动创建的对象实例。
固有对象在任何JS代码执行前就已经被创建出来了,它们通常扮演者类似基础库的角色。我们前面提到的“类”其实就是固有对象的一种。
三个值:
Infinity、NaN、undefined。

九个函数:

eval
isFinite
isNaN
parseFloat
parseInt
decodeURI
decodeURIComponent
encodeURI
encodeURIComponent
一些构造器:
Array、Date、RegExp、Promise、Proxy、Map、WeakMap、Set、WeapSet、Function、Boolean、String、Number、Symbol、Object、Error、EvalError、RangeError、ReferenceError、SyntaxError、TypeError
URIError、ArrayBuffer、SharedArrayBuffer、DataView、Typed Array、Float32Array、Float64Array、Int8Array、Int16Array、Int32Array、UInt8Array、UInt16Array、UInt32Array、UInt8ClampedArray。

四个用于当作命名空间的对象:

Atomics
JSON
Math
Reflect

内置对象·原生对象

我们把 JS 中,能够通过语言本身的构造器创建的对象称作原生对象
在 JS 标准中,提供了30多个构造器。按照不同应用场景,可以把原生对象分成了以下几个种类。

基本类型 基础功能与数据结构 错误类型 二进制操作 带类型的数组
Boolean Array Error ArrayBuffer Float32Array
String Date EvalError SharedArrayBuffer Float64Array
Number RegExp RangeError DataView Int8Array
Symbol Promise ReferenceError Int16Array
Object Proxy SyntaxError Int32Array
Map TypeError Unit8Array
WeakMap URIError Unit16Array
Set Unit32Array
WeakSet Unit8ClampedArray
Function

通过这些构造器,我们可以用new运算创建新的对象,所以我们把这些对象称作原生对象。
几乎所有这些构造器的能力都是无法用纯JavaScript代码实现的,它们也无法用class/extend语法来继承。
函数对象的定义是:具有[[call]]私有字段的对象,构造器对象的定义是:具有私有字段[[construct]]的对象。

包装对象

只有 js 对象才有方法,为什么 number,boolean,string 可以有方法,
null 和 undefined 没有方法
字符串,数字,布尔值有包装对象
String(), Number(), Boolean()
null 和 undefined 没有包装对象

  1. var s = 's';
  2. var S = new String('s')
  3. typeof s // string
  4. typeof S // object
  5. s == S // true == 认为一样
  6. s === S // false
  1. var s = 'hello'
  2. // 这个时候 js 通过new String(s)创建了一个临时对象
  3. s.len = 4 //不会报错
  4. var t = s.len // t is undefined
  5. // 临时创建的对象是包装对象

创建对象的方法

有几种方法,每种方法的特性

字面量

  1. var obj = {}

属性值简写

注意,可计算属性名与简洁表示法,不能同时使用,会报错。
属性值简写是编译时执行的 简单语法糖,可以帮助避免重复

  1. var foo = 'bar'
  2. var baz = {foo} // 直接写入变量,属性名为变量名, 属性值为变量的值
  3. baz // {foo: "bar"}
  4. // 等同于
  5. var baz = {foo: foo};

方法简写

  1. var o = {
  2. method() {
  3. return "Hello!";
  4. }
  5. };
  6. // 等同于
  7. var o = {
  8. method: function() {
  9. return "Hello!";
  10. }
  11. };
  1. // 异步函数generator
  2. let obj = {
  3. * fun() {
  4. }
  5. }
  6. let obj = {
  7. fun : function* fun () {}
  8. }

可计算属性名

声明一个属性名依赖变量或表达式的对象
以前需要这样声明

  1. function getEnvelope(type, description) {
  2. var envelope = {
  3. data: {}
  4. }
  5. envelope[type] = description
  6. return envelope
  7. }
  1. function getEnvelope(type, description) {
  2. return {
  3. data: {},
  4. [type]: description
  5. }
  6. }

注意,属性名表达式如果是一个对象,默认情况下会自动将对象转为字符串[object Object],这一点要特别小心。

  1. var keyA = {a: 1}
  2. var keyB = {b: 2}
  3. const myObject = {
  4. [keyA]: 'valueA',
  5. [keyB]: 'valueB'
  6. };
  7. myObject // Object {[object Object]: "valueB"}

上面代码中,[keyA]和[keyB]得到的都是[object Object],所以[keyB]会把[keyA]覆盖掉,而myObject最后只有一个[object Object]属性。
key 值是 Function 没关系

new

普通

  1. var obj = new func()

工厂模式

  1. function createPerson(name) {
  2. var o = new Object();
  3. o.name = name;
  4. o.getName = function () {
  5. console.log(this.name);
  6. };
  7. return o;
  8. }
  9. var person1 = createPerson('kevin');

缺点:对象无法识别,因为所有的实例都指向一个原型

  • object.create方法创建
  1. var obj2 = Object.create(P);
  2. var obj = Object.create(null) // 不继承任何属性和方法
  3. var obj = Object.create(Object.prototype) // 和 var obj = {}一样

js 类与继承

对象的属性

对象内置属性

[[class]]

所有typeof返回值为“object”的对象(如数组)都包含一个内部属性[[class]],这个属性无法直接访问,一般通过Object.prototype.toString(..)来查看。

  1. console.log(Object.prototype.toString.call([1,2,3])); //[object Array]
  2. [1,2,3].toString() // "1,2,3"

[[prototype]]

chrome 用proto实现,指向委托的原型

对象的三个属性

原型属性
类属性
可扩展性

  1. var obj = {a:1}
  2. Object.has(obj, 'a') //error
  3. Reflect.isExtensible(obj) // true
  4. Object.freeze(obj) // 冻结
  5. obj.z = 3
  6. Reflect.isExtensible(obj) // true

属性的值

不可重复,空字符串或者任意字符串
如果传入对象 变成[object object]
传入函数,f.toString()变成字符串

属性的类型

数据属性

数据属性可以直接赋值,默认特性都是true
数据属性的特性

  • [[Configurable]]:表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特
    性,或者能否把属性修改为访问器属性。像前面例子中那样直接在对象上定义的属性,它们的
    这个特性默认值为 true。
  • [[Enumerable]]:表示能否通过 for-in 循环返回属性。像前面例子中那样直接在对象上定义的属性,它们的这个特性默认值为 true。
  • [[Writable]]:表示能否修改属性的值。像前面例子中那样直接在对象上定义的属性,它们的
    这个特性默认值为 true。
  • [[Value]]:包含这个属性的数据值。读取属性值的时候,从这个位置读;写入属性值的时候,
    把新值保存在这个位置。这个特性的默认值为 undefined。

要修改属性默认的特性,必须使用 ECMAScript 5 的 Object.defineProperty()方法。这个方法 接收三个参数:属性所在的对象、属性的名字和一个描述符对象。其中,描述符(descriptor)对象的属 性必须是:configurable、enumerable、writable 和 value。设置其中的一或多个值,可以修改 对应的特性值。例如:

  1. var person = {}; Object.defineProperty(person, "name", {
  2. writable: false,
  3. value: "Nicholas" });
  4. alert(person.name); //"Nicholas" person.name = "Greg"; alert(person.name); //"Nicholas"

使用Object.defineProperty定义一个变量的时候默认都是 false,
不是修改变量的特性的时候。
把 configurable 设置为 false,

  1. 表示不能从对象中删除属性
  2. 只可以修改特性为 false,

访问器属性

4个特性

  • [[Configurable]]:表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特 性,或者能否把属性修改为数据属性。对于直接在对象上定义的属性,这个特性的默认值为 true。
  • [[Enumerable]]:表示能否通过 for-in 循环返回属性。对于直接在对象上定义的属性,这 5 个特性的默认值为 true。
  • [[Get]]:在读取属性时调用的函数。默认值为 undefined。
  • [[Set]]:在写入属性时调用的函数。默认值为 undefined。
    访问器属性不能直接定义,必须使用 Object.defineProperty()来定义。

使用:
访问器属性的常见方 式,即设置一个属性的值会导致其他属性发生变化。

不一定非要同时指定 getter 和 setter。只指定 getter 意味着属性是不能写,尝试写入属性会被忽略。 在严格模式下,尝试写入只指定了 getter 函数的属性会抛出错误。类似地,只指定 setter 函数的属性也 不能读,否则在非严格模式下会返回 undefined,而在严格模式下会抛出错误。

  1. var cart = {
  2. _wheels: 4,
  3. get wheels () {
  4. return this._wheels;
  5. },
  6. set wheels (value) {
  7. if (value < this._wheels) {
  8. throw new Error('数值太小了!');
  9. }
  10. this._wheels = value;
  11. }
  12. }

[学习es6]setter/getter探究
属性的赋值器(setter)和取值器(getter),事实上也是采用这种写法。

属性的设置和查询

  • 查询对象的属性的时候,在原型链上一层一层查找,找到就返回
  • 对属性 x赋值的时候
    1. 如果有自有属性x,赋值,
    2. 如果没有自有属性x
      1. 如果有继承的属性 x
        1. 继承属性是只读的,不允许赋值,严格模式下,赋值失败会报错
        2. 如果读写的,创建自有属性 x

只有个查询的时候才会体会到继承的存在
设置属性于继承无关

  1. var obj = {a:1}
  2. var obj1 = Object.create(obj)
  3. obj1.a // => 1
  4. // 此时 a 的属性在 obj1的原型上
  5. obj1.a = 2;// obj1没有自有属性a,不会修改原型上的属性的值,创建自有属性a,并赋值
  6. obj1.a // 2

删除属性

只能删除configurable为true自有属性,

  1. var obj = {a:1}
  2. delete obj.a
  3. var a = 1;
  4. a // 1
  5. window.a // 1
  6. delete a // false
  7. // 全局变量不能删除

原型相关

Object.getPrototypeOf()

  1. function Rectangle() {
  2. // ...
  3. }
  4. const rec = new Rectangle();
  5. Object.getPrototypeOf(rec) === Rectangle.prototype
  6. // true
  7. Object.setPrototypeOf(rec, Object.prototype);
  8. Object.getPrototypeOf(rec) === Rectangle.prototype
  9. // false

Object.setPrototypeOf

  1. function setPrototypeOf(obj, proto) {
  2. obj.__proto__ = proto;
  3. return obj;
  4. }
  1. let proto = {};
  2. let obj = { x: 10 };
  3. Object.setPrototypeOf(obj, proto);
  4. proto.y = 20;
  5. proto.z = 40;
  6. obj.x // 10
  7. obj.y // 20
  8. obj.z // 40

检测属性

propertyIsEnumerable

  1. var obj = {x: 1}
  2. obj.propertyIsEnumerable('toString') // false

in

如果自有属性或者继承的属性有就 true

  1. var o = {a:1}
  2. 'a' in o // true
  3. 'b' in o // false
  4. 'toString' in o // true

in运算符还可以用来遍历某个对象的所有属性。

  1. // 所以用 for/in 遍历对象会遍历到继承的属性
  2. var obj = {a:1}
  3. var obj1 = Object.create(obj)
  4. obj1.b = 2
  5. var obj2 = Object.create(obj1)
  6. obj2.c = 3
  7. for(var prop in obj3) {
  8. console.log(prop)
  9. }
  10. // c,b,a

hasOwnProperty()

每个实例对象都有一个hasOwnProperty()方法,用来判断某一个属性到底是本地属性,还是继承自prototype对象的属性。

  1.   alert(cat1.hasOwnProperty("name")); // true
  2.   alert(cat1.hasOwnProperty("type")); // false

isPrototypeOf()

判断继承关系,只要在原型链上就行
X.isPrototypeOf(Y)判断的是X对象是否在Y的原型链上,即判断Y是否继承X。

  1. function M () {}
  2. var obj = new M()
  3. M.prototype.isPrototypeOf(obj)//true
  4. var obj = {a:1}
  5. var obj1 = Object.create(obj)
  6. obj.isPrototypeOf(obj1) //true
  7. Object.prototype.isPrototypeOf(obj1)
  8. Object.prototype.isPrototypeOf(obj)

instanceof

判断实例关系
判断 M 的 prototype 是否在 obj的原型链上,即 obj是否是 M 的实例

  1. function M () {}
  2. var obj = new M()
  3. obj instanceof M //true
  4. obj instanceof Object //true

只要是原型链上的构造函数都是

对象拷贝

  1. const target = {}
  2. const source = { b: 4, c: 5}

实现私有属性

  • es5 很难保护私有属性, 如何设置私有属性
  • es6 setter getter,只读或者控制写入
  1. let _age = 4
  2. class Animal {
  3. constructor (type) {
  4. this.type = type
  5. }
  6. // 加上 get set 就是属性的读取
  7. // age 是出入口
  8. get age () {
  9. return _age
  10. }
  11. set age (val) {
  12. // this.age = val 错误,死循环
  13. if (true) {
  14. // _age 形成了闭包
  15. _age = val
  16. }
  17. }
  18. eat () {}
  19. }
  20. let dog = new Animal('dog')

判断相等

Object.is()
ES5比较两个值是否相等,只有两个运算符:相等运算符()和严格相等运算符(=)。它们都有缺点,前者会自动转换数据类型,后者的NaN不等于自身,以及+0等于-0。JavaScript缺乏一种运算,在所有环境中,只要两个值是一样的,它们就应该相等。

ES6提出“Same-value equality”(同值相等)算法,用来解决这个问题。Object.is就是部署这个算法的新方法。它用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。

  1. Object.is('foo', 'foo')
  2. // true
  3. Object.is({}, {})

// false
不同之处只有两个:一是+0不等于-0,二是NaN等于自身。

  1. +0 === -0 //trueNaN === NaN // false
  2. Object.is(+0, -0) // falseObject.is(NaN, NaN) // true

ES5可以通过下面的代码,部署Object.is。

  1. Object.defineProperty(Object, 'is', {
  2. value: function(x, y) {
  3. if (x === y) {
  4. // 针对+0 不等于 -0的情况 return x !== 0 || 1 / x === 1 / y;
  5. }
  6. // 针对NaN的情况 return x !== x && y !== y;
  7. },
  8. configurable: true,
  9. enumerable: false,
  10. writable: true
  11. });

对象的拷贝

Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。
浅拷贝,如果想要深拷贝可以递归 Object.assign

  1. var target = { a: 1 };
  2. var source1 = { b: 2 };
  3. var source2 = { c: 3 };
  4. Object.assign(target, source1, source2);
  5. target // {a:1, b:2, c:3}
  6. Object.assign方法的第一个参数是目标对象,后面的参数都是源对象。

注意,如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。

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

Object.assign方法实行的是浅拷贝,而不是深拷贝。也就是说,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。

  1. var obj1 = {a: {b: 1}};
  2. var obj2 = Object.assign({}, obj1);
  3. obj1.a.b = 2;
  4. obj2.a.b // 2

对于这种嵌套的对象,一旦遇到同名属性,Object.assign的处理方法是替换,而不是添加。

  1. var target = { a: { b: 'c', d: 'e' } }
  2. var source = { a: { b: 'hello' } }
  3. Object.assign(target, source)

// { a: { b: ‘hello’ } }
// 把 d 的值删除了,所以这是 Object.assign 拷贝的问题,不符合常理

  • 使用场景:
    (1)为对象添加属性
  1. class Point {
  2. constructor(x, y) {
  3. Object.assign(this, {x, y});
  4. }
  5. }

(2)为对象添加方法

  1. Object.assign(SomeClass.prototype, {
  2. someMethod(arg1, arg2) {
  3. ···
  4. },
  5. anotherMethod() {
  6. ···
  7. }
  8. });
  9. // 等同于下面的写法
  10. SomeClass.prototype.someMethod = function (arg1, arg2) {
  11. ···
  12. };
  13. SomeClass.prototype.anotherMethod = function () {
  14. ···
  15. };

实例方法和静态方法

静态方法就是类方法,


  1. function Animal () {
  2. }
  3. Animal.walk() //类方法
  4. // 实例方法
  5. Animal.prototype.eat = function () {
  6. Animal.walk();
  7. }
  8. let dog = new Animal('dog')
  9. dog.walk() //报错
  10. dog.eat() //好的
  11. Animal.walk() // 好的
  12. Animal.eat() // 报错
  1. class Animal {
  2. constructor () {}
  3. eat () {Animal.walk()} // 实例方法
  4. static walk () {}// 类方法
  5. }

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

把对象(关联数组)转成索引数组

Object.keys()

**Object.keys()** 方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致 。

  1. var obj = {a:1,b:2}
  2. Object.keys(obj);
  3. // return ['a','b']
  4. Object.values(obj);
  5. // return [1,2]
  6. Object.entries(obj)
  7. // return [['a',1],['b',2]]