对象就是一组“键值对”(key-value)的集合,是一种无序的复合数据集合。
键名又称为属性或属性名(property)键值又称为属性值(value) //常用写法 let obj = {'name': 'zxc', 'age': 23} //规范写法 let obj2 = new Object({'name': 'zxc', 'age': 23})对象的所有键名都是字符串(ES6 又引入了 Symbol 值也可以作为键名),不是标识符, 可以包含任意字符。引号可省略,省略之后就只能写标识符和纯数字,就算引号省略了, 键名还是字符串。
如果键名是数值,会被自动转为字符串。如果键名不符合标识符的条件(比如第一个字符为数字,或者含有空格或运算符),且也不是数字,则必须加上引号,否则会报错。如果一个属性值为函数,通常把这个属性称为“方法”。如果属性值还是一个对象,就形成了链式引用。对象的属性之间用逗号分隔,最后一个属性后面可以加逗号(trailing comma),也可以不加。
属性可以动态创建,不必在对象声明时就指定。
对象定义时,需要变量作为属性名,属性值需要用[]来包裹住。
let name = 'age' let info = { [name] = 18 } info // {age: 18}由来:
let name = 'age' let info = {} info[name] = '18' info // {age: 18}ES6之前,对象定义完后,变量可以作为属性名,来动态定义属性。因此借鉴这部分用[]方式。
对象采用大括号表示,这导致了一个问题:如果行首是一个大括号,它到底是表达式还是语句?
{ foo: 123 }JavaScript 引擎读到上面这行代码,会发现可能有两种含义。
第一种可能是,这是一个表达式,表示一个包含foo属性的对象;
第二种可能是,这是一个语句,表示一个代码块,里面有一个标签foo,指向表达式123。
疑问: chrome直接输出{ foo: 123 }, 是一个对象. 答: 因为 chrome 欺骗了我们, 用 firefox 可以验证下面这句话,JS是只花了10天就设计出来的语言。
为了避免这种歧义,JavaScript 引擎的做法是,如果遇到这种情况,无法确定是对象还是代码块,一律解释为代码块。
{ console.log(123) } // 123上面的语句是一个代码块,而且只有解释为代码块,才能执行。
如果要解释为对象,最好在大括号前加上圆括号。
因为圆括号的里面,只能是表达式,所以确保大括号只能解释为对象。
({ foo: 123 }) // 正确 ({ console.log(123) }) // 报错这种差异在eval语句(作用是对字符串求值)中反映得最明显。
eval('{foo: 123}') // 123 eval('({foo: 123})') // {foo: 123}读取对象的属性,有两种方法:
点运算符:obj['key']方括号运算符:obj.key请注意,如果使用方括号运算符,键名必须放在引号里面,否则会被当作变量处理(数字键可以不加引号,因为会自动转成字符串)。
方括号运算符内部还可以使用表达式。
注意,数值键名不能使用点运算符(因为会被当成小数点),只能使用方括号运算符。eg: obj.123
点运算符和方括号运算符,不仅可以用来读取值,还可以用来赋值。
批量赋值: let obj = {} Object.assign(obj, {age: 23, gender: 'men'}) 无法通过自身修改或增加共用属性: let obj = {}, obj = {}//共用toString属性 obj.toString = 'xxx'//只会在改obj自身属性 obj2.toString //还是原型上的 偏要修改或增加原型的属性:一般来说不要修改, 会引起很多问题, 这也是JS的脆弱之处。 obj.__proto__toString = 'xxx' //推荐使用下面这种 Object.prototype.toString = 'xxx'//推荐使用这种 修改原型链(隐藏属性): //不推荐使用__proto__这种 let person = {name: 'Jonathan Ben'} let person2 = {name: 'Jonathan Lee'} let common = {country: 'China'} person.__proto__ = common person2.__proto__ = common //推荐使用这种(ES6),规范的写法:要改就一开始就要改, 别后面再改,不然非常影响性能。 //Object.create尽量初始化空对象, 然后再进行追加, 这样方便. let person = Object.create(common) person.name = 'Jonathan Ben' let person2 = Object.create(common) person2.name = 'Jonathan Lee'查看一个对象本身的所有属性,可以使用Object.keys方法。
查看自身所有属性名:Object.keys(obj)
查看自身所有属性值:Object.values(obj)
查看自身以及原型(共用属性):console.dir(obj)推荐这种, obj.__proto__推荐前面这种
判断一个属性是自身的还是原型的: obj.hasOwnProperty('toString')
原型:
原型里面存着对象的共用属性比如obj的原型就是一个对象obj.__proto__存着这个对象的地址原型也是对象:
原型也是有原型的原型是对象的跟虽然原型有原型, 但是为null let obj = {} obj.__proto__ // {constructor: ƒ, __defineGetter__: ƒ, ... } obj.__proto__.__proto__ // null '__proto__' in obj.__proto__ // truedelete命令用于删除对象的属性,删除成功后返回true。
var obj = { p: 1 }; Object.keys(obj) // ["p"] delete obj.p // true obj.p // undefined Object.keys(obj) // []注意,删除一个不存在的属性,delete不报错,而且返回true。因此,不能根据delete命令的结果,认定某个属性是存在的。
var obj = {}; delete obj.p // true只有一种情况,delete命令会返回false,那就是该属性存在,且不能删除。
var obj = Object.defineProperty({}, 'p', { value: 123, configurable: false }); obj.p // 123 delete obj.p // false上面代码之中,对象obj的p属性是不能删除的,所以delete命令返回false(关于Object.defineProperty方法的介绍,请看《标准库》的 Object 对象一章)。
另外,需要注意的是,delete命令只能删除对象本身的属性,无法删除继承的属性(关于继承参见《面向对象编程》章节)。
var obj = {}; delete obj.toString // true obj.toString // function toString() { [native code] }可以通过in来判断对象有哪些属性。
let obj = { 'name': 'ben' } 'name' in obj // true 'age' in obj // false不能用obj.xxx === undefined 来判断xxx是否为obj的属性,这只能判断属性的值为undefined。
它的作用是操作同一个对象的多个属性时,提供一些书写的方便。
// 例一 var obj = { p1: 1, p2: 2, }; with (obj) { p1 = 4; p2 = 5; } // 等同于 obj.p1 = 4; obj.p2 = 5; // 例二 with (document.links[0]){ console.log(href); console.log(title); console.log(style); } // 等同于 console.log(document.links[0].href); console.log(document.links[0].title); console.log(document.links[0].style);注意,如果with区块内部有变量的赋值操作,必须是当前对象已经存在的属性,否则会创造一个当前作用域的全局变量。
var obj = {}; with (obj) { p1 = 4; } obj.p1 // undefined p1 // 4这是因为with区块没有改变作用域,它的内部依然是当前作用域。这造成了with语句的一个很大的弊病,就是绑定对象不明确。
with (obj) { console.log(x); }单纯从上面的代码块,根本无法判断x到底是全局变量,还是对象obj的一个属性。这非常不利于代码的除错和模块化,编译器也无法对这段代码进行优化,只能留到运行时判断,这就拖慢了运行速度。
建议不要使用with语句,可以考虑用一个临时变量代替with。
with(obj1.obj2.obj3) { console.log(p1 + p2); } // 可以写成 var temp = obj1.obj2.obj3; console.log(temp.p1 + temp.p2);对象的隐藏属性:每一个对象都有隐藏属性,隐藏属性中存储这共用属性组成的对象的地址,共用属性组成的对象叫做原型,也就是说, 隐藏属性存储着原型的地址。
简单基本类型:string、number、boolean、null和undefined
复杂基本类型:函数、数组、内置函数(String、Number、Boolean、Object、Function、Array、Date、RegExp 和 Error) 疑问:symbol呢?
null 有时会被当做一种对象类型,但是这其实只是 JavaScript 语言本身的一个 bug,即对 null执行typeof(null)时会返回字符串"object"。实际上,'null本身是简单基础类型。原因是这样的,不同的对象在底层都表示为二进制,在 JavaScript 中二进制前三位都为 0 的话会被判 断为 object 类型,null 的二进制表示是全 0,自然前三位也是 0,所以执行 typeof 时会返回"object"。