什么是对象
对象是JavaScript的根本数据类型。是属性的无序汇合,每个属性都是一个键值对。属性又分为两种:一种是数据属性,一种是办法属性。(另一种说法是:对象是键值对的汇合,对象是由属性和办法形成的)
对象属性名是字符串,所以对象又能够看成是字符串到值的映射。字符串到值的映射这种构造,通常还有一些其它的叫法,比方:散列表、字典、关联数组。
JavaScript对象是动静的,咱们能够对对象执行新增属性,删除属性的操作。在JS当中除了字符串、数字、true、false、null、undefined这些值之外,其它所有的值都是对象。
对象的创立形式
对象间接量
创建对象最简略的形式就是应用对象间接量。比方如下代码:
// 创立一个空对象const o1 = {};// 创立一个带有属性和办法的对象const o2 = { name: "thirteen", class: "class", age: 18, say: function () { console.log("console.log: say hello"); }};console.log(o2.class, "关键字当做属性名");o2.say();
new 运算符(构造函数)
new 运算符创立并初始化一个新对象,new运算符前面紧跟一个函数调用。new 后追随的函数被成为构造函数。
应用构造函数创建对象
let o = new Object(); // 创立一个空对象,和{}(对象间接量)一样let a = new Array(); // 创立一个空数组对象,和[]一样function papa(){};let child = new papa(); // 通过实例化构造函数papa来创立一个对象。
Object.create
Object.create办法是ES5中新增创建对象的一个办法。该办法有两个参数,第一个参数必选是新创建对象的原型,第二个参数是对对象属性的形容。Object.create办法返回一个新的对象
语法
Object.create(proto,[propertiesObject])
// 创立没有原型的新对象const o1 = Object.create(null);// 从该办法的介绍中咱们能够看到,第一个参数是新创建对象的原型,// o1创立的时候传入null,则示意o1是没有原型的新对象,没有原型,也就没有继承任何的办法和属性。// 创立一个具备原型的对象const o2 = Object.create(Object.prototype); // 相当于{}和new Object()创建对象一样// 创立一个以另一个空对象为原型,且领有一个属性p的对象const o3 = Object.create({}, { p: { value: 42 } })const 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); } }});// 创立一个继承现有对象的新对象const person = { name:"thirteen", age:18, say:function(){ console.log(`name:${this.name},age:${this.age} `); }}const o3 = Object.create(person);o3.name === "thirteen" // trueo3.age === 18 // true o3.say(); // name:thirteen,age:18
用Object.create实现类式继承
// 父类function parent(){ this.x = 0; this.y = 0;}// 父类的办法parent.prototype.move = function(x,y){ this.x += x; this.y += y; console.log('move 执行了');}// 子类function child(){ parent.call(this);}// 子类继承父类child.prototype = Object.create(parent.prototype);child.prototype.constructor = child;// 实例化var o1 = new child();console.log(o1 instanceof child);console.log(o1 instanceof parent);o1.move(2,2);
对象的属性
一个JavaScript对象有很多属性。一个对象的属性能够被解释成一个附加到对象上的变量。对象属性和一般的JavaScript变量根本没有什么区别,仅仅是属性属于某个对象。
属性查问和设置(批改)
属性的查问和设置有两种形式,第一种是应用(•)点运算符.,第二种是应用([])方括号运算符。在ES3,应用"."点运算符前面是不能跟JS当中的保留字的,例如:for,class,function等,不过在ES5之后就不存在这种问题了,所以上面的代码在古代浏览器外面是不会有问题的.obj.for = "for"
。如果应用方括号[],方括号两头在ES6之前必须是一个计算结果为字符串的表达式。在古代浏览器当中,方括号表达式两头还能够是Symbol类型。
点运算符
// 定义一个对象o1const o1 = { name:"thirteen", age:18}// 读取对象o1的属性o1.name // thirteeno1.age // 18// 批改对象o1的属性,如果属性名原来就有,就是批改,如果原来对象上没有该属性,就是新增o1.sex = "男";o1.say = function(){ console.log('i say');}console.log(o1.name);// const name = "thirteen";const age = 18;const o2 = { name, age}o2.name // thirteeno2.age // 18
方括号运算符
当通过[]来拜访(新增,批改)对象的属性时,属性名通过字符串来示意。字符串是JS的根本数据类型,在程序运行时咱们能够批改和创立他们。因为有方括号这种拜访对象的形式,有时候对象也被叫做关联数组,只不过数组的索引是数字,对象的索引是字符串。
const addr = "";const obj = { p0:"p0", p1:"p1", p2:"p2", p3:"p3"};for(i=0;i<4;i++){ addr += obj["p"+i] + '\n';}// 当对象的属性名字无奈确认(动静)的时候,只能通过方括号拜访function addStock(portfolio,name,shares){ portfolio[name] = shares;}//同时创立四个变量,用逗号宰割const myObj = new Object(), str = "myString", rand = Math.random(), obj = new Object();myObj.type = "点运算符";myObj['data create'] = '有空格的字符串';myObj[str] = "字符串值";myObj[rand] = "随机数";myObj[obj] = "Object 对象";myObj[""] = "空字符串";// PS:方括号中所有的键都将转换为字符串类型。
两种形式比拟
点运算符
- 使用方便灵便
- 有肯定的限度,当对象的属性是数字结尾的字符串、空字符串、连字符就不能应用点运算符来拜访
- 点运算符前面紧跟的标识符是动态的,必须写死在程序中
方括号运算符
- 应用全面,所有点运算符能拜访的不能拜访的属性,方括号都能够拜访
- 当对象的属性是无奈提前得悉的时候,只能应用方括号运算符
属性的删除
delete 运算符能够删除对象的属性;不过只能删除对象本身的属性,不能删除对象继承的属性。另外,如果对象的属性的可配置值是false的时候,也是不可能被删除的。delete 表达式执行胜利后,会返回一个true
const o1 = { name:"thirteen"}Object.defineProperty(o1, 'age', { value: 42, configurable: false});// 能够删除delete o1.name console.log(o1.name) // 返回undefined// 不能够删除,因为属性的可配置值configurable是falsedelete o1.ageconsole.log(o1.age) // 返回42// 不能够删除delete o1.toString
属性的枚举
属性遍历的秩序
- 先遍历数字键,依照谁小谁先的形式
- 再遍历字符串键,依照在对象中的地位排列
- 最初遍历Symbol,依照退出工夫升序排列。
// 对象属性遍历的秩序Reflect.ownKeys({ [Symbol()]:0, b:0, 10:0, 2:0, a:0})// 返回后果['2','10','a','b',Symbol()]
for...in循环
该办法顺次拜访一个对象以及其原型链中所有可枚举的属性
const o = { x: 1, y: 2, z: 3,};// 对象 o1 继承 对象oconst o1 = Object.create(o, { foo: { writable: true, configurable: true, enumerable:true, // 能够被枚举 value: "hello", }, // bar会成为所创建对象的拜访器属性 bar: { configurable: false, get: function () { return 10; }, set: function (value) { console.log("Setting `o.bar` to", value); }, },});console.log("打印对象o1的属性名和属性值");for (prop in o1) { console.log(`${prop}:${o1[prop]}`);}// foo:hello x:1 y:2 z:3// 给对象o1新增a属性,属性可枚举配置为true 可能遍历失去Object.defineProperty(o1, "a", { value: "4", enumerable: true,});// 给对象o1新增b属性,属性可枚举配置为false 不能可能遍历失去Object.defineProperty(o1, "b", { value: "5", enumerable: false,});console.log("打印对象o1可枚举的属性名和属性值");for (prop in o1) { console.log(`${prop}:${o1[prop]}`);}//foo:hello x:1 y:2 z:3 a:4
Object.keys(obj)
该办法返回对象obj本身蕴含(不包含原型中)的所有可枚举属性的名称数组
const o = { x: 1, y: 2, z: 3,};// 对象o1继承o,const o1 = Object.create(o);o1.name = "thirteen";o1.age = 18;const resultArray = Object.keys(o1);console.log(resultArray,'打印对象可枚举属性的名称数组');// [name,age]// 给对象o1新增a属性,属性可枚举配置为true 可能遍历失去Object.defineProperty(o1, "a", { value: "4", enumerable: true,});// 给对象o1新增b属性,属性可枚举配置为false 不能可能遍历失去Object.defineProperty(o1, "b", { value: "5", enumerable: false,});const resultArray1 = Object.keys(o1);console.log(resultArray1,'打印对象可枚举属性的名称数组');// [name,age,a]
Object.getOwnPropertyNames(obj)
该办法返回对象obj本身蕴含(不包含原型中)的所有属性(无论是否可枚举)的名称的数组。
const o = { x: 1, y: 2, z: 3,};const o1 = Object.create(o, { sex: { enumerable: false, value: "男", }, height: { enumerable: true, value: 165, },});o1.name = "thirteen";o1.age = 18;const resultArray = Object.getOwnPropertyNames(o1);console.log(resultArray, "打印对象可枚举属性的名称数组");// [sex,height,name,age]// 给对象o1新增a属性,属性可枚举配置为true 可能遍历失去Object.defineProperty(o1, "a", { value: "4", enumerable: true,});// 给对象o1新增b属性,属性可枚举配置为false 不能可能遍历失去Object.defineProperty(o1, "b", { value: "5", enumerable: false,});const resultArray1 = Object.getOwnPropertyNames(o1);console.log(resultArray1, "打印对象可枚举属性的名称数组");// [sex,height,name,age,a,b]
Object.getOwnPropertySymbols(obj)
返回一个数组,蕴含对象obj本身所有Symbol属性的键名
const mySymbol = Symbol("mySymbol");const mySymbol1 = Symbol("mySymbol1");const mySymbol2 = Symbol("mySymbol2");const mySymbol3 = Symbol("mySymbol3");const o = { [mySymbol]: 1, y: 2, z: 3,};const o1 = Object.create(o, { sex: { enumerable: false, value: "男", }, height: { enumerable: true, value: 165, },});o1.name = "thirteen";o1.age = 18;o1[mySymbol1] = "mySymbol1";const resultArray = Object.getOwnPropertySymbols(o1);console.log(resultArray, "打印对象Symbol属性的键名数组");// [symbol(mySymbol1)]// 给对象o1新增a属性,属性可枚举配置为true 可能遍历失去Object.defineProperty(o1, mySymbol2, { value: "4", enumerable: true,});// 给对象o1新增b属性,属性可枚举配置为false 不能可能遍历失去Object.defineProperty(o1, mySymbol3, { value: "5", enumerable: false,});const resultArray1 = Object.getOwnPropertySymbols(o1);console.log(resultArray1, "打印对象Symbol属性的键名数组");// [symbol(mySymbol1),symbol(mySymbol2),symbol(mySymbol3)]
Reflect.ownKeys(obj)
返回一个数组,蕴含对象本身所有键名,不论键名是Symbol或字符串,也不论是否可枚举。
const mySymbol = Symbol("mySymbol");const mySymbol1 = Symbol("mySymbol1");const mySymbol2 = Symbol("mySymbol2");const mySymbol3 = Symbol("mySymbol3");const o = { [mySymbol]: 1, y: 2, z: 3,};const o1 = Object.create(o, { sex: { enumerable: false, value: "男", }, height: { enumerable: true, value: 165, },});o1.name = "thirteen";o1.age = 18;o1[mySymbol1] = "mySymbol1";const resultArray = Reflect.ownKeys(o1);console.log(resultArray, "打印对象本身所有属性的键名数组");// [sex,height,name,age,Symbol(mySymbol1)]// 给对象o1新增a属性,属性可枚举配置为true 可能遍历失去Object.defineProperty(o1, mySymbol2, { value: "4", enumerable: true,});// 给对象o1新增b属性,属性可枚举配置为false 不能可能遍历失去Object.defineProperty(o1, "b", { value: "5", enumerable: false,});const resultArray1 = Reflect.ownKeys(o1);;console.log(resultArray1, "打印对象Symbol属性的键名数组");// [ "sex", "height", "name", "age", "b", Symbol(mySymbol1), Symbol(mySymbol2) ]
属性的个性
除了蕴含名字和值之外,属性还蕴含一些标记他们可写,可枚举和可配置的个性。在ES3中无奈设置这些内容,ES5之后就能够了。属性能够分为『存取器属性』(getter和setter定义的属性)和『数据属性』,不同的属性有不同的个性。
存取器属性
- get // 读取
- set // 写入
- enumerable // 是否能够被枚举(遍历读取)
- configurable // 当且仅当该属性的 configurable 键值为 true 时,该属性的描述符才可能被扭转,同时该属性也能从对应的对象上被删除。
数据属性
- value // 属性值
- writable // 属性是否可写(是否能够批改)
- enumerable // 是否能够被枚举(遍历读取)
- configurable
获取属性的属性形容
通过调用Object.getOwnPropertyDescriptor办法能够取得某个对象特定属性的属性形容,例如:
const o = { x: "我是X", y: 2, z: 3,};const o1 = Object.create(o);o1.age = 18;Object.defineProperty(o1, "name", { value: "thirteen", writable: true, configurable: true, enumerable: true,});const ageDesc = Object.getOwnPropertyDescriptor(o1, "age");const nameDesc = Object.getOwnPropertyDescriptor(o1, "name");const xDesc = Object.getOwnPropertyDescriptor(o1, "x");//console.log(ageDesc, "获取自有属性age默认的属性形容");console.log(nameDesc, "获取自有属性name配置的属性形容");console.log(xDesc, "获取继承属性X的属性形容");// 如果想要获取继承的属性的属性形容,须要通过Object.getPrototypeOf办法先遍历原型链const xdesc2 = Object.getOwnPropertyDescriptor( Object.getPrototypeOf(o1), "x");console.log(xdesc2, "获取继承属性X的属性形容");
属性getter和setter
对象的属性是由名字、值和一组个性形成的。在ES5之后属性的值能够用一个或两个办法代替,这两个办法就是getter和setter。由getter和setter定义的属性称作『存取器属性』,不同于『数据属性』,数据属性只有一个简略的值。
当咱们应用getter和setter定义属性的时候,咱们就能够监听属性的读取和写入动作,比方VUE框架当中就应用了Object.defineProperty办法重写了data对象当中的所有属性,这样当data的值产生扭转的时候,就能够更新vue当中的模板了;
存取器属性还能够智能查看属性的写入值,以及在属性每次读取的时候返回不同的值;
// 如何定义,第一种const o1 = { name: "thirteen", $age: 18, get age() { return this.$age; }, set age(v) { this.$age = v; },};console.log("第一种返回");console.log(o1.age); // 18o1.age = 20;console.log(o1.age); // 20// 第二种const o2 = { $age: 18,};let ageValue = 18;Object.defineProperty(o2, "age", { get: function () { return ageValue; }, set: function (value) { ageValue = value; },});console.log("第二种返回");console.log(o2.age); // 18o2.age = 20;console.log(o2.age); // 20// 第三种:当应用Object.create 办法创建对象的时候const o3 = 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); }, },});console.log("第三种返回");console.log(o3.bar); // 10o3.bar = 20; // 打印 『Setting o.bar to 20』// 读取对象属性时,每次返回不同的值const serialnum = { $n: 0, get next() { return this.$n++; }, set next(n) { if (n >= this.$n) this.$n = n; else throw "序列号的值不能比以后值小"; },};console.log("序列号属性返回")console.log(serialnum.next); // 0console.log(serialnum.next); // 1console.log(serialnum.next); // 2console.log(serialnum.next); // 3console.log(serialnum.next); // 4console.log(serialnum.next); // 5// 每次读取属性都返回一个随机数const random = { get octet() { // 返回0~255之间的数字 return Math.floor(Math.random() * 256); }, get uint16() { // 返回0~65535之间的数字 return Math.floor(Math.random() * 65536); }, get int16() { // 返回-32768~(65535-32768)之间的数字 return Math.floor(Math.random() * 65536) - 32768; },};console.log("随机数打印后果")console.log(random.octet);console.log(random.octet);console.log(random.octet);console.log(random.uint16);console.log(random.uint16);console.log(random.uint16);console.log(random.int16);console.log(random.int16);console.log(random.int16);
对象的办法
当咱们通过对象间接量、new Object()和Object.create()不传入null的时候创立的对象,都会和另一个对象关联,『另一个』对象就是咱们熟知的原型,每一个对象都从原型继承属性;
所以当咱们新创建一个对象的同时,对象就有一些办法能够应用,这些办法就是从原型继承的。
toString
返回一个示意调用这个办法的对象值的字符串。
toLocaleString
同toString相似,不同的是针对不同类型的对象做了定制解决,比方Data和Number对象
valueOf
和toString相似,经常在JavaScript须要将对象转换为某种原始值而非字符串的时候才会调用它。
其它
new Object()
通过构造函数的模式创建对象,new Object()办法可承受一个参数;上面看一些例子:
const o = new Object();console.log(o,'不传入任何参数');const o1 = new Object(null);console.log(o1,'传入null');const o2 = new Object(undefined);console.log(o2,'传入undefined');// 相当于 const o3 = new Boolean(true);const o3 = new Object(true); console.log(o3,'传入true');// 相当于 const o4 = new String("3");const o4 = new Object("3");console.log(o4,'传入字符串');const o5 = new Object(function(){});console.log(o5,'传入函数');const o6 = new Object(function(){ return [1,2,3]});console.log(o6,'传入函数');// 相当于 const o4 = new Boolean(false);const o7 = new Object(Boolean());console.log(o7,'传入对象');
打印后果:
super关键字
对象当中的this指向以后所在的对象,ES6中新增了一个super关键字,super关键字指向对象的原型对象;
const parent = { name: 'papa'};const child = { name: 'child', say() { return super.name; }};// 通过setProtoTypeOf 设置对象child的原型是parentObject.setPrototypeOf(child, parent);child.say() // "papa"
PS:留神,super关键字示意原型对象时,只能用在对象的办法之中,用在其余中央都会报错。
详情参考:ES6——对象的扩大
更多JavaScript对象的扩大内容,参考阮一峰的ES6对象的扩大一节