Javacsript对象

概述

对象中包含一系列属性,这些属性是无序的。每个属性都有一个字符串key和对应的value。属性名可以是包含空字符串在内的任意字符串,对象中不能存在两个同名的属性。值可以是任意javascript值,或者可以是一个getter或setter函数。除了名字和值之外,每个属性还有一些与之相关的值,称为“属性特性”;除了包含属性之外,每个对象还拥有3个相关的对象特性,如下图所示:
屏幕快照 2021-06-13 下午3.01.52.png

创建对象

可以通过对象直接量、关键字new和Object.create()函数来创建对象。

对象直接量

let obj1 = {x:1, y:2}; 
let obj2 = { 
  x:1,
  y:2,
  z: { 
    m:'a',
    n:'b'
  }
}; 
复制代码

关键字new创建对象(原型链)

new运算符创建并初始化一个新对象。关键字new后面跟随一个函数调用。这里的函数称做构造函数,构造函数用以初始化一个新创建的对象。

function foo(){}
foo.prototype.z = 3;

var obj = new foo();
obj.y = 2;
obj.x = 1;

obj.x; // 1
obj.y; // 2
obj.z; // 3
typeof obj.toString; // ‘function'
'z' in obj; // true
obj.hasOwnProperty('z'); // false

obj.z = 5;

obj.hasOwnProperty('z'); // true
foo.prototype.z; // still 3
obj.z; // 5

obj.z = undefined;
obj.z; // undefined

delete obj.z; // true
obj.z; // 3

delete obj.z; // true
obj.z; // still 3!!!

复制代码

屏幕快照 2021-06-13 下午3.02.39.png

Object.create()

Object.create()创建一个新对象,其中第一个参数是这个对象的原型,第二个参数可选,用以对对象的属性进行进一步描述。

var obj = Object.create({x:1, y:2}); // obj继承了属性x和y

obj.x // 1
typeof obj.toString // "function"
obj.hasOwnProperty('x') // false
复制代码

屏幕快照 2021-06-12 下午9.40.00.png
可以通过传入参数null来创建一个没有原型的新对象,但是通过这种方式创建的对象不会集成任何东西,甚至不包括基础方法,比如toString(),也就是说,它将不能和’+’运算符一起正常工作。

var obj = Object.create(null); 

obj.toString // undefined 
复制代码

屏幕快照 2021-06-12 下午9.43.44.png
如果想创建一个普通的空对象(比如通过{}或new Object()创建的对象),需要传入Object.prototype:

var obj = Object.create(Object.prototype); // obj和{}和new Object()一样 
复制代码

属性操作

属性的查询与设置(读写属性)

  • 可以通过(.)或方括号([])运算符来获取属性的值或者给属性赋值。
var obj = {x: 1, y: 2};

obj.x; // 1
obj["y"]; // 2

obj["x"] = 3;
obj.y = 4;
复制代码
  • javascript对象具有“自有属性”,也有一些属性是从原型对象继承而来。假设要查询对象o的属性x,如果o中不存在x,那么将会继续在o的原型对象中查询属性x。如果原型对象中也没有x,但这个原型对象也有原型,那么继续在这个原型对象的原型上执行查询,直到找到x或者找到一个原型是null的对象为止。可以看到,对象的原型构成了一个“链”,通过这个“链”可以实现属性的继承。
  • 现在假设给对象o的属性x赋值,如果o中已经有属性x(这个属性不是继承而来的),那么这个赋值操作只改变这个已有属性的值。如果o中不存在属性x,那么赋值操作给o添加一个新属性x。如果之前o继承自属性x,那么这个继承的属性就被新创建的同名属性覆盖。
  • 属性赋值操作首先检查原型链,以此判定是否允许赋值操作。例如,如果o继承自一个只读属性x,那么赋值操作是不允许的。如果允许属性赋值操作,它也总是在原始对象上创建属性或对已有的属性赋值,而不会去修改原型链。
  • 在javascript中,只有查询属性才会体会到继承的存在,而设置属性则和继承无关。
  • 如果o继承自属性x,而这个属性是一个具有setter方法的accessor属性,那么这时将调用setter方法而不是给o创建一个属性x。需要注意的是,setter方法是由对象o调用的,而不是定义这个属性的原型对象调用的。因为如果setter方法定义任意属性,这个操作只针对o本身,并不会修改原型链。
function foo() {}

Object.defineProperty(foo.prototype, 'z', {
    get: function(){
       return 1;
    }
});

var obj = new foo();

obj.z; // 1
obj.z = 10; // obj继承自属性z,而这个属性如果存在get/set方法,给当前对象赋值会失败。
obj.z; // still 1

Object.defineProperty(obj, 'z', {value : 100, configurable: true}); 
// 此时如果想给当前对象添加新属性z,只能通过defineProperty方式
obj.z; // 100;
delete obj.z;
obj.z; // back to 1

复制代码

屏幕快照 2021-06-13 下午3.03.19.png

var o = {};
Object.defineProperty(o, 'x', {value : 1}); // writable=false, configurable=false
var obj = Object.create(o);
obj.x; // 1
obj.x = 200; // writable为false不可写,赋值失败
obj.x; // still 1, can't change it

Object.defineProperty(obj, 'x', {writable:true, configurable:true, value : 100});
obj.x; // 100
obj.x = 500;
obj.x; // 500

复制代码


删除属性

  • delete运算只能删除自有属性,不能删除继承属性(要删除继承属性必须从定义这个属性的原型对象上删除它,而且这会影响到所有继承自这个原型的对象)。
  • 当delete表达式删除成功或没有任何副作用(比如删除不存在的属性)时,它返回true。如果delete后不是一个属性访问表达式,delete同样返回true。
  • delete不能删除那些可配置性为false的属性。
var person = {age : 28, title : 'fe'};
delete person.age; // 删除属性age,返回true
delete person['title']; // 删除属性title,返回true
person.age; // undefined
delete person.age; // 什么都没做(age属性已经不存在了),返回true
delete person.gender; // 什么都没做(gender属性不存在),返回true
delete person.toString; // 什么都没做(toString是继承而来的),返回true
delete 1; // 无意义,返回true

delete Object.prototype; // false, 属性是不可配置

var descriptor = Object.getOwnPropertyDescriptor(Object, 'prototype');
descriptor.configurable; // false

复制代码

检测属性

javascript对象可以看做属性的集合,我们经常会检测集合中成员的所属关系——判断某个属性是否存在于摸个对象中。可以通过in运算符、hasOwnProperty()和propertyIsEnumerable()方法来完成这个工作。

  • in运算符的左侧是属性名,右侧是对象。如果对象自有属性或继承属性中包含这个属性则返回true;
  • 对象的hasOwnProperty()方法用来检测给定的名字是否是对象的自有属性,对于继承属性返回fasle。
  • propertyIsEnumerable是hasOwnProperty()的增强版,只有检测是自有属性且这个属性的可枚举性为true时它才返回true。
let cat = new Object;
cat.legs = 4;
cat.name = "Kitty";

'legs' in cat; // true
'abc' in cat; // false
"toString" in cat; // true, inherited property!!!

cat.hasOwnProperty('legs'); // true
cat.hasOwnProperty('toString'); // false

cat.propertyIsEnumerable('legs'); // true
cat.propertyIsEnumerable('toString'); // false

Object.defineProperty(cat, 'price', {enumerable : false, value : 1000});
cat.propertyIsEnumerable('price'); // false
cat.hasOwnProperty('price'); // true

复制代码

枚举属性

除了检测对象的属性是否存在,我们还会经常遍历对象的属性。

类型 特点
Object.keys(obj) 返回对象本身可直接枚举的属性(不含Symbol属性)
Object.values(obj) 返回对象本身可直接枚举的属性值(不含Symbol属性)
Object.entries(obj) 返回对象本身可枚举属性键值对相对应的数组(不含Symbol属性)
Object.getOwnPropertyNames(obj) 返回对象所有自身属性的属性名(不包括Symbol值作为名称的属性)
Object.getOwnPropertySymbols(obj) 返回一个给定对象自身的所有 Symbol 属性的数组
for……in 所有可枚举的属性(包括原型上的)
for……of 必须部署了Iterator接口后才能使用,例如数组、Set和Map结构、类数组对象、Generator对象以及字符串
forEach break不能中断循环
Reflect.ownKeys(obj) 对象自身所有属性

上述遍历对象的属性时都遵循同样的属性遍历次序规则:

  1. 首先遍历所有属性名为数值的属性,按照数字排序
  2. 其次遍历所有属性名为字符串的属性,按照生成时间排序
  3. 最后遍历所有属性名为Symbol值的属性,按照生成时间排序
const Obj = {
    [Symbol(0)]: 'symbol',
    1 : '1',
    'c': 'c',
    '1a1': '11',
    22223333: '2',
    'd': 'd'
};

console.log(Reflect.ownKeys(Obj)); // [ '1', '22223333', 'c', '1a1', 'd', Symbol(0) ]
复制代码

getter、setter

另一种读写属性的方式。

var man = {
    name : 'Bosn',
    weibo : '@Bosn',
    get age() {
        return new Date().getFullYear() - 1988;
    },
    set age(val) {
        console.log('Age can\'t be set to ' + val);
    }
}
console.log(man.age); // 27
man.age = 100; // Age can't be set to 100
console.log(man.age); // still 27

复制代码

属性标签

  • 可写(writable):表明是否可以设置该属性的值。
  • 可枚举(enumerable):表明是否可以通过for/in循环返回该属性。
  • 可配置(configurable):表明是否可以删除或修改该属性。
  1. 数据属性:value、writable、enumerable、configurable
Object.getOwnPropertyDescriptor({pro : true}, 'pro');
// Object {value: true, writable: true, enumerable: true, configurable: true}
Object.getOwnPropertyDescriptor({pro : true}, 'a'); // undefined

var person = {};
Object.defineProperty(person, 'name', {
    configurable : false,
    writable : false,
    enumerable : true,
    value : "Bosn Ma"
});

person.name; // Bosn Ma
person.name = 1; // writable为fasle,赋值失败
person.name; // still Bosn Ma
delete person.name; // false,// configurable为fasle,删除失败

Object.defineProperty(person, 'type', {
    configurable : true,
    writable : true,
    enumerable : false,
    value : "Object"
});

Object.keys(person); // ["name"],name的enumerable为true, type的enumerable为false

Object.defineProperties(person, {
    title : {value : 'fe', enumerable : true},
    corp : {value : 'BABA', enumerable : true},
    salary : {value : 50000, enumerable : true, writable : true}
});

Object.getOwnPropertyDescriptor(person, 'salary');
// Object {value: 50000, writable: true, enumerable: true, configurable: false}
Object.getOwnPropertyDescriptor(person, 'corp');
// Object {value: "BABA", writable: false, enumerable: true, configurable: false}

复制代码
  1. 存取器属性:get、set、enumerable、configurable
Object.defineProperties(person, {
    title: {value : 'fe', enumerable : true},
    corp: {value : 'BABA', enumerable : true},
    salary: {value : 50000, enumerable : true, writable : true},
    luck: {
        get : function() {
        return Math.random() > 0.5 ? 'good' : 'bad';
        }
    },
    promote: {
        set : function (level) {
            this.salary *= 1 + level * 0.1;
        }
    }
});

Object.getOwnPropertyDescriptor(person, 'salary');
// Object {value: 50000, writable: true, enumerable: true, configurable: false}
Object.getOwnPropertyDescriptor(person, 'corp');
// Object {value: "BABA", writable: false, enumerable: true, configurable: false}
person.salary; // 50000
person.promote = 2;
person.salary; // 60000

复制代码

可写控制着对值特性的修改。可配置性控制着对其他特性(包括属性是否可以删除)的修改。然而规则远不止这么简单,例如,如果属性是可配置的话,则可以修改不可写属性的值。同样,如果属性是不可配置的,仍然可以将属性修改为不可写属性。以下是完整的规则:

  • 如果对象是不可扩展的,则可以编辑已有的自有属性,但不能给它添加新属性。
  • 如果属性是不可配置的,则不能修改他的可配置性和可枚举性。
  • 如果存取器属性是不可配置的,则不能修改其getter和setter方法,也不能将它转换为数据属性。
  • 如果数据属性是不可配置的,则不能将它转换为存取器属性。
  • 如果数据属性是不可配置的,则不能将它的可写性从false修改为true,但可以从true修改为false。
  • 如果数据属性是不可配置且不可写的,则不能修改它的值。然而可配置但不可写属性的值是可以修改的(实际上是先将它标记为可写,然后修改它的值,最后转化为不可写)。

总结:
屏幕快照 2021-06-13 下午2.10.41.png

对象标签

  • 对象的原型([[proto]]):指向另外一个对象,本对象的属性继承自它的原型对象。

屏幕快照 2021-06-13 下午3.04.45.png

  • 对象的类([[class]]):是一个标识对象类型的字符串。
var toString = Object.prototype.toString;
function getType(o){
  return toString.call(o).slice(8,-1);
};

toString.call(null); // "[object Null]"
getType(null); // "Null"
getType(undefined); // "Undefined"
getType(1); // "Number"
getType(new Number(1)); // "Number"
typeof new Number(1); // "object"
getType(true); // "Boolean"
getType(new Boolean(true)); // "Boolean"

复制代码
  • 对象的扩展标记([[extensible]]):指明了是否可以向该对象添加新属性。
var obj = {x : 1, y : 2};
Object.isExtensible(obj); // true
Object.preventExtensions(obj);
Object.isExtensible(obj); // false
obj.z = 1;
obj.z; // undefined, add new property failed
Object.getOwnPropertyDescriptor(obj, 'x');
// Object {value: 1, writable: true, enumerable: true, configurable: true}

Object.seal(obj);
Object.getOwnPropertyDescriptor(obj, 'x');
// Object {value: 1, writable: true, enumerable: true, configurable: false}
Object.isSealed(obj); // true

Object.freeze(obj);
Object.getOwnPropertyDescriptor(obj, 'x');
// Object {value: 1, writable: false, enumerable: true, configurable: false}
Object.isFrozen(obj); // true

// [caution] not affects prototype chain!!!

复制代码

序列化对象

对象序列化是指将对象的状态转换为字符串,也可以将字符串还原为对象,可通过JSON.stringify()和JSON.parse()操作。这些方法都使用JSON作为数据交换格式。JSON的语法是javascript语法的子集,它并不能表示javascript里的所有值:

  • 支持对象、数组、字符串、无穷大数字、true、false、null,并且他们可以序列化和还原。
  • NaN、Infinity和-Inifity序列化的结果是null,日期对象序列化的结果是ISO格式的日期字符串。
  • 函数、RegExp、Error对象和undefined值不能序列化和还原。
  • JSON.stringify()只能序列化对象可枚举的自有属性。
let obj = {x : 1, y : true, z : [1, 2, 3], nullVal : null};
JSON.stringify(obj); // "{"x":1,"y":true,"z":[1,2,3],"nullVal":null}"

obj = {val : undefined, a : NaN, b : Infinity, c : new Date()};
JSON.stringify(obj); // "{"a":null,"b":null,"c":"2015-01-20T14:15:43.910Z"}"

obj = JSON.parse('{"x" : 1}');
obj.x; // 1

复制代码

对象方法

let obj = {x : 1, y : 2};
obj.toString(); // "[object Object]"
obj.toString = function() {return this.x + this.y};
"Result " + obj; // "Result 3", by toString

+obj; // 3, from toString

obj.valueOf = function() {return this.x + this.y + 100;};
+obj; // 103, from valueOf

"Result " + obj; // still "Result 3"

复制代码


参考资料

Javascript继承机制的设计思想
对象方法-MDN
遍历对象的9种方法

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享