乐趣区

关于javascript:JavaScript-对象

什么是对象

对象是 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" // true
o3.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 类型。

点运算符

// 定义一个对象 o1
const o1 = {
    name:"thirteen",
  age:18
}

// 读取对象 o1 的属性
o1.name // thirteen
o1.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 // thirteen
o2.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:方括号中所有的键都将转换为字符串类型。

两种形式比拟

点运算符

  1. 使用方便灵便
  2. 有肯定的限度,当对象的属性是数字结尾的字符串、空字符串、连字符就不能应用点运算符来拜访
  3. 点运算符前面紧跟的标识符是动态的,必须写死在程序中

方括号运算符

  1. 应用全面,所有点运算符能拜访的不能拜访的属性,方括号都能够拜访
  2. 当对象的属性是无奈提前得悉的时候,只能应用方括号运算符

属性的删除

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 是 false
delete o1.age

console.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 继承 对象 o
const 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); // 18
o1.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); // 18
o2.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); // 10
o3.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); // 0
console.log(serialnum.next); // 1
console.log(serialnum.next); // 2
console.log(serialnum.next); // 3
console.log(serialnum.next); // 4
console.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 的原型是 parent
Object.setPrototypeOf(child, parent);
child.say() // "papa"

PS:留神,super 关键字示意原型对象时,只能用在对象的办法之中,用在其余中央都会报错。
详情参考:ES6——对象的扩大
更多 JavaScript 对象的扩大内容,参考阮一峰的 ES6 对象的扩大一节

退出移动版