介绍
除了 字符串,数字,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 没有包装对象
var s = 's';
var S = new String('s')
typeof s // string
typeof S // object
s == S // true == 认为一样
s === S // false
var s = 'hello'
// 这个时候 js 通过new String(s)创建了一个临时对象
s.len = 4 //不会报错
var t = s.len // t is undefined
// 临时创建的对象是包装对象
创建对象的方法
有几种方法,每种方法的特性
字面量
var obj = {}
属性值简写
注意,可计算属性名与简洁表示法,不能同时使用,会报错。
属性值简写是编译时执行的 简单语法糖,可以帮助避免重复
var foo = 'bar'
var baz = {foo} // 直接写入变量,属性名为变量名, 属性值为变量的值
baz // {foo: "bar"}
// 等同于
var baz = {foo: foo};
方法简写
var o = {
method() {
return "Hello!";
}
};
// 等同于
var o = {
method: function() {
return "Hello!";
}
};
// 异步函数generator
let obj = {
* fun() {
}
}
let obj = {
fun : function* fun () {}
}
可计算属性名
声明一个属性名依赖变量或表达式的对象
以前需要这样声明
function getEnvelope(type, description) {
var envelope = {
data: {}
}
envelope[type] = description
return envelope
}
function getEnvelope(type, description) {
return {
data: {},
[type]: description
}
}
注意,属性名表达式如果是一个对象,默认情况下会自动将对象转为字符串[object Object],这一点要特别小心。
var keyA = {a: 1}
var keyB = {b: 2}
const myObject = {
[keyA]: 'valueA',
[keyB]: 'valueB'
};
myObject // Object {[object Object]: "valueB"}
上面代码中,[keyA]和[keyB]得到的都是[object Object],所以[keyB]会把[keyA]覆盖掉,而myObject最后只有一个[object Object]属性。
key 值是 Function 没关系
new
普通
var obj = new func()
工厂模式
function createPerson(name) {
var o = new Object();
o.name = name;
o.getName = function () {
console.log(this.name);
};
return o;
}
var person1 = createPerson('kevin');
缺点:对象无法识别,因为所有的实例都指向一个原型
- object.create方法创建
var obj2 = Object.create(P);
var obj = Object.create(null) // 不继承任何属性和方法
var obj = Object.create(Object.prototype) // 和 var obj = {}一样
对象的属性
对象内置属性
[[class]]
所有typeof返回值为“object”的对象(如数组)都包含一个内部属性[[class]],这个属性无法直接访问,一般通过Object.prototype.toString(..)来查看。
console.log(Object.prototype.toString.call([1,2,3])); //[object Array]
[1,2,3].toString() // "1,2,3"
[[prototype]]
对象的三个属性
原型属性
类属性
可扩展性
var obj = {a:1}
Object.has(obj, 'a') //error
Reflect.isExtensible(obj) // true
Object.freeze(obj) // 冻结
obj.z = 3
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。设置其中的一或多个值,可以修改 对应的特性值。例如:
var person = {}; Object.defineProperty(person, "name", {
writable: false,
value: "Nicholas" });
alert(person.name); //"Nicholas" person.name = "Greg"; alert(person.name); //"Nicholas"
使用Object.defineProperty定义一个变量的时候默认都是 false,
不是修改变量的特性的时候。
把 configurable 设置为 false,
- 表示不能从对象中删除属性
- 只可以修改特性为 false,
访问器属性
4个特性
- [[Configurable]]:表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特 性,或者能否把属性修改为数据属性。对于直接在对象上定义的属性,这个特性的默认值为 true。
- [[Enumerable]]:表示能否通过 for-in 循环返回属性。对于直接在对象上定义的属性,这 5 个特性的默认值为 true。
- [[Get]]:在读取属性时调用的函数。默认值为 undefined。
- [[Set]]:在写入属性时调用的函数。默认值为 undefined。
访问器属性不能直接定义,必须使用 Object.defineProperty()来定义。
使用:
访问器属性的常见方 式,即设置一个属性的值会导致其他属性发生变化。
不一定非要同时指定 getter 和 setter。只指定 getter 意味着属性是不能写,尝试写入属性会被忽略。 在严格模式下,尝试写入只指定了 getter 函数的属性会抛出错误。类似地,只指定 setter 函数的属性也 不能读,否则在非严格模式下会返回 undefined,而在严格模式下会抛出错误。
var cart = {
_wheels: 4,
get wheels () {
return this._wheels;
},
set wheels (value) {
if (value < this._wheels) {
throw new Error('数值太小了!');
}
this._wheels = value;
}
}
[学习es6]setter/getter探究
属性的赋值器(setter)和取值器(getter),事实上也是采用这种写法。
属性的设置和查询
- 查询对象的属性的时候,在原型链上一层一层查找,找到就返回
- 对属性 x赋值的时候
- 如果有自有属性x,赋值,
- 如果没有自有属性x
- 如果有继承的属性 x
- 继承属性是只读的,不允许赋值,严格模式下,赋值失败会报错
- 如果读写的,创建自有属性 x
- 如果有继承的属性 x
只有个查询的时候才会体会到继承的存在
设置属性于继承无关
var obj = {a:1}
var obj1 = Object.create(obj)
obj1.a // => 1
// 此时 a 的属性在 obj1的原型上
obj1.a = 2;// obj1没有自有属性a,不会修改原型上的属性的值,创建自有属性a,并赋值
obj1.a // 2
删除属性
只能删除configurable为true自有属性,
var obj = {a:1}
delete obj.a
var a = 1;
a // 1
window.a // 1
delete a // false
// 全局变量不能删除
原型相关
Object.getPrototypeOf()
function Rectangle() {
// ...
}
const rec = new Rectangle();
Object.getPrototypeOf(rec) === Rectangle.prototype
// true
Object.setPrototypeOf(rec, Object.prototype);
Object.getPrototypeOf(rec) === Rectangle.prototype
// false
Object.setPrototypeOf
function setPrototypeOf(obj, proto) {
obj.__proto__ = proto;
return obj;
}
let proto = {};
let obj = { x: 10 };
Object.setPrototypeOf(obj, proto);
proto.y = 20;
proto.z = 40;
obj.x // 10
obj.y // 20
obj.z // 40
检测属性
propertyIsEnumerable
var obj = {x: 1}
obj.propertyIsEnumerable('toString') // false
in
如果自有属性或者继承的属性有就 true
var o = {a:1}
'a' in o // true
'b' in o // false
'toString' in o // true
in运算符还可以用来遍历某个对象的所有属性。
// 所以用 for/in 遍历对象会遍历到继承的属性
var obj = {a:1}
var obj1 = Object.create(obj)
obj1.b = 2
var obj2 = Object.create(obj1)
obj2.c = 3
for(var prop in obj3) {
console.log(prop)
}
// c,b,a
hasOwnProperty()
每个实例对象都有一个hasOwnProperty()方法,用来判断某一个属性到底是本地属性,还是继承自prototype对象的属性。
alert(cat1.hasOwnProperty("name")); // true
alert(cat1.hasOwnProperty("type")); // false
isPrototypeOf()
判断继承关系,只要在原型链上就行
X.isPrototypeOf(Y)判断的是X对象是否在Y的原型链上,即判断Y是否继承X。
function M () {}
var obj = new M()
M.prototype.isPrototypeOf(obj)//true
var obj = {a:1}
var obj1 = Object.create(obj)
obj.isPrototypeOf(obj1) //true
Object.prototype.isPrototypeOf(obj1)
Object.prototype.isPrototypeOf(obj)
instanceof
判断实例关系
判断 M 的 prototype 是否在 obj的原型链上,即 obj是否是 M 的实例
function M () {}
var obj = new M()
obj instanceof M //true
obj instanceof Object //true
只要是原型链上的构造函数都是
对象拷贝
const target = {}
const source = { b: 4, c: 5}
实现私有属性
- es5 很难保护私有属性, 如何设置私有属性
- es6 setter getter,只读或者控制写入
let _age = 4
class Animal {
constructor (type) {
this.type = type
}
// 加上 get set 就是属性的读取
// age 是出入口
get age () {
return _age
}
set age (val) {
// this.age = val 错误,死循环
if (true) {
// _age 形成了闭包
_age = val
}
}
eat () {}
}
let dog = new Animal('dog')
判断相等
Object.is()
ES5比较两个值是否相等,只有两个运算符:相等运算符()和严格相等运算符(=)。它们都有缺点,前者会自动转换数据类型,后者的NaN不等于自身,以及+0等于-0。JavaScript缺乏一种运算,在所有环境中,只要两个值是一样的,它们就应该相等。
ES6提出“Same-value equality”(同值相等)算法,用来解决这个问题。Object.is就是部署这个算法的新方法。它用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。
Object.is('foo', 'foo')
// true
Object.is({}, {})
// false
不同之处只有两个:一是+0不等于-0,二是NaN等于自身。
+0 === -0 //trueNaN === NaN // false
Object.is(+0, -0) // falseObject.is(NaN, NaN) // true
ES5可以通过下面的代码,部署Object.is。
Object.defineProperty(Object, 'is', {
value: function(x, y) {
if (x === y) {
// 针对+0 不等于 -0的情况 return x !== 0 || 1 / x === 1 / y;
}
// 针对NaN的情况 return x !== x && y !== y;
},
configurable: true,
enumerable: false,
writable: true
});
对象的拷贝
Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。
浅拷贝,如果想要深拷贝可以递归 Object.assign
var target = { a: 1 };
var source1 = { b: 2 };
var source2 = { c: 3 };
Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}
Object.assign方法的第一个参数是目标对象,后面的参数都是源对象。
注意,如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。
var target = { a: 1, b: 1 };
var source1 = { b: 2, c: 2 };
var source2 = { c: 3 };
Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}
Object.assign方法实行的是浅拷贝,而不是深拷贝。也就是说,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。
var obj1 = {a: {b: 1}};
var obj2 = Object.assign({}, obj1);
obj1.a.b = 2;
obj2.a.b // 2
对于这种嵌套的对象,一旦遇到同名属性,Object.assign的处理方法是替换,而不是添加。
var target = { a: { b: 'c', d: 'e' } }
var source = { a: { b: 'hello' } }
Object.assign(target, source)
// { a: { b: ‘hello’ } }
// 把 d 的值删除了,所以这是 Object.assign 拷贝的问题,不符合常理
- 使用场景:
(1)为对象添加属性
class Point {
constructor(x, y) {
Object.assign(this, {x, y});
}
}
(2)为对象添加方法
Object.assign(SomeClass.prototype, {
someMethod(arg1, arg2) {
···
},
anotherMethod() {
···
}
});
// 等同于下面的写法
SomeClass.prototype.someMethod = function (arg1, arg2) {
···
};
SomeClass.prototype.anotherMethod = function () {
···
};
实例方法和静态方法
静态方法就是类方法,
function Animal () {
}
Animal.walk() //类方法
// 实例方法
Animal.prototype.eat = function () {
Animal.walk();
}
let dog = new Animal('dog')
dog.walk() //报错
dog.eat() //好的
Animal.walk() // 好的
Animal.eat() // 报错
class Animal {
constructor () {}
eat () {Animal.walk()} // 实例方法
static walk () {}// 类方法
}
Object.keys(),Object.values(),Object.entries()
Object.keys()
**Object.keys()**
方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致 。
var obj = {a:1,b:2}
Object.keys(obj);
// return ['a','b']
Object.values(obj);
// return [1,2]
Object.entries(obj)
// return [['a',1],['b',2]]