转载请注明出处

原文连接 http://blog.huanghanlian.com/article/5b698ed6b8ea642ea9213f4c

对象中包含一系列属性,这些属性是无序的。每个属性都有一个字符串key和对应的value。

JavaScript 对象概述

概述

对象中包含一系列属性,这些属性是无序的。每个属性都有一个字符串key和对应的value。

var obj = {x : 1, y : 2}; //定义obj对象, 有两个属性x和yobj.x; // 1 //访问对应obj.x属性取到对应的值obj.y; // 2 //访问对应obj.y属性取到对应的值

这里有两个重点,一个是属性是无序的,再一个每一个key是字符串。

探索对象的key

var obj = {}; //定义对象objobj[1] = 1; //动态赋值数字1属性值为1obj['1'] = 2; //动态赋值字符串1属性值为2console.log(obj); // Object {1: 2} //实际上结果指向的是同一个属性//每个属性都有一个字符串key和对应的value。obj[{}] = true; //[{}] 空对象作为keyobj[{x : 1}] = true; //对象带有属性的对象作为key //js都会把它转换成字符串然后再去处理,最终指向的是同一个属性console.log(obj)// Object {1: 2, [object Object]: true}

回顾-数据类型

函数,数组,日期,正则等都是对象

对象结构

var obj = {}; //字面量空对象obj.y = 2; //赋值创建属性xobj.x = 1; //赋值创建属性y

对象有个特点,他的属性可以动态的添加或删除的

函数对象

function foo(){};console.log(foo.prototype);

创建一个函数声明
每一个函数都会有一个foo.prototype对象属性

function foo(){};foo.prototype.z=1;console.log(foo.prototype);

foo.prototype对象属性添加属性z赋值为1,foo.prototype对象属性下有个constructor属性方法,该属性其实是指向自己本身。

function foo(){};foo.prototype.z=1;console.log(foo.prototype);var obj =new foo();console.log(obj);

通过new去实例化foo()函数var obj =new foo();那么这个obj原型就会指向构造器的prototype属性也就是foo.prototype,可以通过上图看出打印输出是一样的。

JavaScript 创建对象,原型链

对象创建-字面量

var obj1 = {x : 1, y : 2};

对象字面量 ,用{}设置属性

var obj2 = {x : 1,y : 2,o : {z : 3,n : 4}};

对象字面量也可以做一些嵌套,比如某些属性值可以又是对象

创建对象-new/原型链

还有一种就是使用new构造器的方法创建对象,先来了解javascript中的原型链。

什么是原型链

function foo(){} //定义函数对象 foo.prototype.z = 3; //函数对象默认带foo.prototype对象属性 对象添加z属性=3var obj =new foo(); //用构造器方式构造新的对象obj.y = 2; //通过赋值添加2个属性给objobj.x = 1; //通过new去构造这样一个对象他的主要特点是,他的原型会指向构造器的foo.prototype属性obj.x; // 1 //访问obj.x发现对象上有x返回1obj.y; // 2 //访问obj.y发现对象上有x返回2obj.z; // 3 //obj上没有z并不会停止查找,会去查找他的原型foo.prototype.z返回3typeof obj.toString; // ‘function' 这是一个函数,toString是Object.prototype上面的每个对象都有'z' in obj; // true obj.z是从foo.prototype继承而来的,所以说obj里面有zobj.hasOwnProperty('z'); // false 表示z并不是obj直接对象上的,而是对象原型链上的。

但是如果是赋值的话结果就一样了。

比如我们赋值obj.z=5

如果给obj.z尝试去赋值,就不会像原型链上去查找了。先看obj.z有没有,有的话修改它的值,没有的话,在对象上添加obj.z

obj.z = 5;obj.hasOwnProperty('z'); // truefoo.prototype.z; // still 3obj.z; // 5obj.z = undefined;obj.z; // undefined

那么怎样能拿到原型链上的z呢

delete obj.z; // true 删除对象上的z属性obj.z; // 3 //这样就能获取原型链上的z

对象创建-Object.create

从字面量理解是对象创建

Object.create({x:1});是系统内置的函数,这个函数会接收一个参数,一般是一个对象。他会返回一个新创建的对象,并且让这个对象的原型指向参数,参数一般是个对象。

var obj = Object.create({ x: 1});console.log(obj.x) // 1console.log(typeof obj.toString) // "function"console.log(obj.hasOwnProperty('x')); // falseconsole.dir(obj)

JavaScript 属性操作

读写对象属性

var obj = { x: 1, y: 2};console.log(obj.x); // 1console.log(obj["y"]); // 2obj["x"] = 3;obj.y = 4;

创建对象字面量obj,有xy两个属性,可以用obj.y(点)操作符去访问他的属性,也可以用obj["y"][中括号这里需要是字符串]

var obj = { x1: 1, x2: 2};var i = 1, n = 2;for (; i <= n; i++) { console.log(obj['x' + i]);}// 输出: 1, 2

obj['x']使用方法,obj['x']同等于obj.x。obj['x']可以在[]内拼接字符串,obj.x+""不是那么方便

var obj = { x1: 1, x2: 2};var p;for (p in obj) { console.log(obj[p]);}

用for...in遍历所有属性
需要注意的是用for...in去遍历的话,有可能把原型链上的属性也遍历出来,并且他的顺序是不确定的。

属性读写-异常

var obj = { x: 1};//创建对象objconsole.log(obj.y); //如果访问一个不存在的属性会进行原型链查找如果找到末端还是找不到就会返回 undefinedvar yz = obj.y.z; //不能获取undefined的属性z TypeError: Cannot read property 'z' of undefinedobj.y.z = 2; //不能给undefined的属性z去赋值 TypeError: Cannot set property 'z' of undefined

var yz;if (obj.y) { yz = obj.y.z;}//判断当obj.y存在把他取出来//巧用运算符var yz = obj && obj.y && obj.y.z;

属性删除

var person = { age: 28, title: 'fe'};delete person.age; // true 表示删除成功delete person['title']; // true 表示删除成功person.age; // undefineddelete person.age; // truedelete Object.prototype; // false, 不允许删除//通过getOwnPropertyDescriptor方法去获取一个属性中所有的标签,第一个参数是你要查看的对象,第二个是你要去检测的属性这样就能拿到属性的描述器var descriptor = Object.getOwnPropertyDescriptor(Object, 'prototype');console.log(descriptor);

属性检测

var cat = new Object; //使用new 构造对象cat.legs = 4; //赋值属性legs值为4cat.name = "Kitty"; //赋值属性name值为Kitty'legs' in cat; // true 表示cat中有legs属性 in操作符是会向原型链上查找的'abc' in cat; // false 表示cat中没有abc属性"toString" in cat; // true, 继承 property有toString属性cat.hasOwnProperty('legs'); // true 表示cat直接量属性有legscat.hasOwnProperty('toString'); // false 表示cat直接量属性没有toString toString是在原型有cat.propertyIsEnumerable('legs'); // true legs可枚举 查看是否可枚举propertyIsEnumerable for...in会被循环cat.propertyIsEnumerable('toString'); // false toString不可枚举 查看是否可枚举propertyIsEnumerable for...in不会被循环

自定义对象属性,让他的枚举标签是false

var cat = new Object; //使用new 构造对象cat.legs = 4; //赋值属性legs值为4cat.name = "Kitty"; //赋值属性name值为Kitty'legs' in cat; // true 表示cat中有legs属性 in操作符是会向原型链上查找的'abc' in cat; // false 表示cat中没有abc属性"toString" in cat; // true, 继承 property有toString属性cat.hasOwnProperty('legs'); // true 表示cat直接量属性有legscat.hasOwnProperty('toString'); // false 表示cat直接量属性没有toString toString是在原型有cat.propertyIsEnumerable('legs'); // true legs可枚举 查看是否可枚举propertyIsEnumerable for...in会被循环cat.propertyIsEnumerable('toString'); // false toString不可枚举 查看是否可枚举propertyIsEnumerable for...in不会被循环//通过Object.defineProperty给cat目标对象去添加一个属性price//enumerable:是否能在for...in循环中遍历出来或在Object.keys中列举出来。//value:属性的值给属性赋值Object.defineProperty(cat, 'price', {enumerable: false, value: 1000});cat.propertyIsEnumerable('price'); // false price不可枚举 查看是否可枚举propertyIsEnumerable for...in不会被循环cat.hasOwnProperty('price'); // true 表示cat直接量属性有priceconsole.log(cat)for(var k in cat){ console.log(cat[k]);//循环出来看看能不能被枚举}

判断对象属性是否存在进行操作

if (cat && cat.legs) { cat.legs *= 2;}//8

判断cat是否存在且cat.legs不是undefined的时候,让cat.legs值乘等于2,表示cat.legs乘以2再赋值给cat.legs

if (cat.legs !== undefined) { // only if cat.legs is not undefined}

判断cat.legs不等于undefined的时候去做动作

if (cat.legs !== undefined) { // only if cat.legs is not undefined}

判断cat.legs严格不等于undefined的时候去做动作

属性枚举

var o = { x: 1, y: 2, z: 3};'toString' in o; // trueo.propertyIsEnumerable('toString'); // falsevar key;for (key in o) { console.log(key); // x, y, z}

这样写出来的对象原型链属性默认是不可枚举的,所以for....in的时候原型链不会出来。

var o = { x: 1, y: 2, z: 3};var obj = Object.create(o); //创建obj对象以o对象作为原型obj.a = 4;console.log(obj)var key;for (key in obj) { console.log(key); // a, x, y, z}

创建obj对象以o对象作为原型
所有对象上的属性,和原型上的属性都会遍历中显示出来

那有时候我只想处理对象上的属性,不想遍历我对象原型链上的属性。

我们需要在加一个obj.hasOwnProperty(key)的判断,来过滤掉原型链上的属性就可以了。

var o = { x: 1, y: 2, z: 3};var obj = Object.create(o); //创建obj对象以o对象作为原型obj.a = 4;var key;for (key in obj) { if (obj.hasOwnProperty(key)){ console.log(key); // a }}JavaScript get/set方法

JavaScript get/set方法

var man = { name: 'huang', weibo: '@.com', get age() { return new Date().getFullYear() - 1993; }, set age(val) { console.log('年龄不能设置' + val); }}console.log(man.age); //23man.age = 100; //年龄不能设置100console.log(man.age); //23

定义了一个对象man有属性name值是huang,这里定义了一对getset方法,来去访问属性age

语法

getset关键字开头,空格,然后是属性的名字,然后是括号再方括号,里面是一个函数体,

get方法会返回当前日期的年份减去我的出生日,会拿到我的年龄。

set方法会拿到括号赋值的值,唯一的参数创进来。

当访问console.log(man.age); //23会返回get方法,

当使用man.age = 100;也就是说给set去赋值的时候,就会去调用set方法,就会输出年龄不能设置100

再去输出console.log(man.age); //23会发现仍然是23,因为set没做任何的事情。

var man = { name: 'huang', $age: null, get age() { if (this.$age == undefined) { return new Date().getFullYear() - 1993; } else { return this.$age; } }, set age(val) { val = +val; if (!isNaN(val) && val > 0 && val < 150) { this.$age = +val; } else { throw new Error('Incorrect=' + val); } }}console.log(man.age); //23man.age = 100;console.log(man.age); //100man.age = 'abn';console.log(man.age);//Uncaught Error: Incorrect=NaN(…)

get/set与原型链

function foo() {};Object.defineProperty(foo.prototype, 'z', { get: function() { return 1; }});var obj = new foo();console.log(obj.z); //1obj.z = 10;console.log(obj.z); //还是输出1Object.defineProperty(obj, 'z', { value: 100, configurable: true});console.log(obj.z);//100delete obj.z;console.log(obj.z);//1

解释

function foo() {};

创建一个foo函数,就会有一个foo.prototype

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

通过definePropertygit方法来创建z属性git方法只是固定的返回1。

var obj = new foo();

使用new方式来创建一个新的对象obj,这样这个obj对象的原型会指向foo.prototype

console.log(obj.z); //1

当我们去访问obj.z的时候obj对象上没有z直接量属性,所以他会向上去查找,foo.prototype原型,发现有一个z属性用的是get方法,这样就能返回1,

obj.z = 10;console.log(obj.z); //还是输出1

那么我赋值,尝试给z赋值为10,如果z不是用get方式赋值的是通过foo.prototype.z原型赋值的话,再使用直接量去赋值是可以成功的,值会返回到obj这个对象上,但是在这里赋值并没有成功。仍然返回1。

这是因为当obj对象上没有z直接量属性的时候,并且他的原型链查找发现有getset方法的时候,那么当我们尝试去赋值的时候,实际上会走原型上getset方法的,而不会再去通过给当前对象obj添加新属性。

Object.defineProperty(obj, 'z', { value: 100, configurable: true});

通过Object.definePropertyobj目标对象去添加一个属性z

value:属性的值给属性赋值writable:如果为false,属性的值就不能被重写。get: 一旦目标属性被访问就会调回此方法,并将此方法的运算结果返回用户。set:一旦目标属性被赋值,就会调回此方法。configurable:如果为false,则任何尝试删除目标属性或修改属性以下特性(writable, configurable, enumerable)的行为将被无效化。enumerable:是否能在for...in循环中遍历出来或在Object.keys中列举出来。

console.log(obj.z);//100

这个时候obj.z是obj的直接量返回100

delete obj.z;

由于configurable: true所以删除是成功的

console.log(obj.z);//1

这里因为直接量z被删除们说以他会继续去向上查找zget方法。

小案例

var o={z:2};Object.defineProperty(o,'x',{value:1});//默认writable=false只读,configurable=false不可写o.z=6;o.x=6;console.log(o);

案例解释

var o={};Object.defineProperty(o,'x',{value:1});//默认writable=false只读,configurable=false不可写var obj=Object.create(o);//创建obj对象以o对象作为原型console.log(obj.x);//1 会向上查找返回原型链x属性值1obj.x=200; //尝试去赋值200console.log(obj.x);//赋值后打印任然是1//那么如何去在obj对象添加x属性呢?//还是要用Object.defineProperty//给obj对象添加一个自己的属性x,这样就可以覆盖掉原型链上的不可写的x,writable:true,configurable:true,value:100Object.defineProperty(obj,'x',{writable:true,configurable:true,value:100});console.log(obj.x);//100 obj.x是直接量属性obj.x=500; //直接量属性可以赋值console.log(obj.x);//500 //修改成功JavaScropt属性标签

属性标签

怎样去看某一个对象上的属性上都有哪些标签

console.log(Object.getOwnPropertyDescriptor({pro:true}, 'pro'));检查一个字面量的对象,添加属性`pro:true`看字面量下的属性`pro`的属性标签//configurable:true,enumerable:true,value:true,writable:trueconsole.log(Object.getOwnPropertyDescriptor({pro:true}, 'a'));//去查一个根本不存在的属性返回undefined//undefined

通过Object.getOwnPropertyDescriptor(参数1,"参数2")方法可以返回一个对象,这个对象上会显示 当前这个属性下所有的标签,这个函数会接收两个参数,第一个是你要去判断的对象,第二个是一个字符串的属性名,

value:属性的值给属性赋值writable:如果为false,属性的值就不能被重写。get: 一旦目标属性被访问就会调回此方法,并将此方法的运算结果返回用户。set:一旦目标属性被赋值,就会调回此方法。configurable:如果为false,则任何尝试删除目标属性或修改属性以下特性(writable, configurable, enumerable)的行为将被无效化。enumerable:是否能在for...in循环中遍历出来或在Object.keys中列举出来

console.log(Object.getOwnPropertyDescriptor({pro:true}, 'pro'));检查一个字面量的对象,添加属性`pro:true`看字面量下的属性`pro`的属性标签

得到的结果是

//configurable:true,enumerable:true,value:true,writable:true

那么怎么去设置或者说设置对象的属性标签呢?

var person = {};Object.defineProperty(person, 'name', { configurable: false, writable: false, enumerable: false, value: "继小鹏"});console.log(person.name); //继小鹏person.price = 100; //修改不了console.log(person.name); //还是继小鹏console.log(delete person.name); //false 不可删除

解释例子

创建一个空对象

var person = {};

通过Object.defineProperty(参数1,"参数2",{参数1})第一个参数是要添加属性的对象,第二个参数是一个字符串属性的名字,第三个参数是一个对象,这个对象里面就是具体每一个标签的值,

Object.defineProperty(person, 'name', { configurable: false,//不可删除 writable: false,//属性的值就不能被重写。 enumerable: true,//可枚举 value: "继小鹏"//属性的值给属性赋值});

结果符合预期

console.log(person.name); //继小鹏person.price = 100; //修改不了console.log(person.name); //还是继小鹏console.log(delete person.name); //false 不可删除

创建新的属性使用Object.keys

var person = {};Object.defineProperty(person, 'name', { configurable: false, writable: false, enumerable: true, value: "继小鹏"});Object.defineProperty(person, 'type', { configurable: true, writable: true, enumerable: false, value: "继小鹏222"});console.log(person);console.log(Object.keys(person));

新创建的属性enumerable: false,不可枚举,可以通过Object.keys(person)的方法来去获取对象上所有的key
因为新创建的属性enumerable: false,不可枚举所有不可见。

使用Object.defineProperties()定义多个对象属性标签

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

Object.defineProperties()这个函数接收两个参数,第一个参数是你要定义属性的对象,第二个参数是一个对象,对象里包含属性值列表集。

例子

var person = {};Object.defineProperties(person, { title: { value: 'fe', enumerable: true, }, corp: { value: 'SDF', 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; } }});console.log(Object.getOwnPropertyDescriptor(person, 'salary'));console.log(Object.getOwnPropertyDescriptor(person, 'corp'));console.log(person.salary);person.promote = 3;console.log(person.salary);console.log(person.luck);

JavaScropt对象标签、对象序列化

对象标签

对象级别也是有标签的,主要有三种

[[proto]]

[[class]]

[[extensible]]

原型标签__proto__

__proto__实际上就是原型,比如说当我们用new构造器的方式去实例化函数对象,那么这个实例化对象的原型链会指向构造器的prototype,一般的对象也会有原型,指向 Object.prototype然后在往上就是nall

class标签

var toString = Object.prototype.toString;function getType(o) { return toString.call(o).slice(8, -1);};console.log(toString.call(null)); //[object Null]console.log(getType(null)); //Nullconsole.log(getType(undefined)); //Undefinedconsole.log(getType(1)); //Numberconsole.log(getType(new Number(1))); //Numberconsole.log(typeof new Number(1)); //objectconsole.log(getType(true)); //Booleanconsole.log(getType(new Boolean(true))); //Boolean

class标签是没有一个直接的方式去查看他或者是修改他,可以间接的通过Object.prototype.toString;的方式来去获取class,

var toString = Object.prototype.toString;

先把Object.prototype.toString;这样一个函数拿到,赋值给toString这样方便后面代码简短一些,

function getType(o) { return toString.call(o).slice(8, -1);};

定义一个方法,返回toString.call(o) 来调用这个函数方法,并且把参数o作为this传进去,然后用slice(8, -1);表示是截取从第八个字符开始,一直到最后。

调用getType(1); //NumbergetType(1)方法会返回类型。

extensible标签

extensible标签表示你这个对象是否可扩展,言外之意就是说对象上的属性是否可以被继续添加。

var obj={x:1,y:2};//创建对象Object.isExtensible(obj);//true

创建对象obj,通过Object.isExtensible(obj);来判断对象是否可扩展,一般情况下默认返回true。表示可以扩展。

那么怎么样不让他扩展,不可修改不可删除冻结对象

var obj={x:1,y:2};//创建对象Object.isExtensible(obj);//true obj对象可扩展Object.preventExtensions(obj); //设置obj对象不可扩展Object.isExtensible(obj);//false obj对象不可扩展obj.d=2; //尝试给obj添加新的属性会发现添加失败console.log(obj.d);//undefined//如果已经组织对象可扩展,那么对象上的属性标签是否会发生变化呢?console.log(Object.getOwnPropertyDescriptor(obj, 'x'));//value: 1, writable: true, enumerable: true, configurable: true//虽然我们阻止了对象不可添加新属性,但是已有熟悉仍然可以修改和删除,也是可以枚举的。//可以通过Object.seal(obj);//会把对象上的属性的configurable设置为false不可删除console.log(Object.getOwnPropertyDescriptor(obj, 'x'));//value: 1, writable: true, enumerable: true, configurable: falseconsole.log(Object.isSealed(obj));//true 判断对象是否被隐藏不可删除Object.freeze(obj);//让对象冻结不可写console.log(Object.getOwnPropertyDescriptor(obj, 'x'));//value: 1, writable: false, enumerable: true, configurable: falseconsole.log(Object.isFrozen(obj));//true 判断对象是否被不可写

序列化

var obj = { x: 1, y: true, z: [1, 2, 3], nullval: null};console.log(obj);console.log(JSON.stringify(obj)); //{"x":1,"y":true,"z":[1,2,3],"nullval":null}var obj1 = { val: undefined, a: NaN, b: Infinity, c: Date()};console.log(obj1);console.log(JSON.stringify(obj1)); //{"a":null,"b":null,"c":"Fri Dec 16 2016 22:25:04 GMT+0800 (中国标准时间)"}var obj2 = JSON.parse('{"x":1}');console.log(obj2); //Object {x: 1}console.log(obj2.x); //1

定义obj 字面量对象,有一些属性,通过JSON.stringify(obj));方法返回一个字符串{"x":1,"y":true,"z":[1,2,3],"nullval":null}返回的字符串就是这个对象的序列化的结果。

需要注意一点这个序列化是有一些坑的,如果你的属性值是undefined的话,那么就不会出现在序列化的结果当中。

如果后端返回一个JSON数据我们怎么把他转换成js对象呢?

使用JSON.parse('{"x":1}');方法,需要注意的是合法的json属性必须以双引号引起来。

序列化-自定义

var obj = { x: 1, y: 2, o: { o1: 1, o2: 2, toJSON: function() { return this.o1 + this.o2; } }};console.log(JSON.stringify(obj)); //{"x":1,"y":2,"o":3}

obj对象下有个属性o他的值是一个对象,那么这个对象序列化的结果我可能想要自定义,那么我们只需要在这个当前的层级下写一个toJSON他的值是一个函数,toJSON是固定这个key一定要这样写,然后这个函数会返回return this.o1 + this.o2;这个this会指向当前层级的数也就是o,

这时候去JSON.stringify(obj));的时候,这里面的o通过toJSON这样一个方法来去返回的。

其他对象方法

var obj = { //定义对象objx: 1,y: 2};obj.toString(); //"[object Object]" 调用对象的toString方法会返回"[object Object]"这样的字符串实际上没有太大意义

//定义自己对象上个的toString方法
obj.toString = function() {
return this.x + this.y; //返回对象的x和y相加作为结果
}

"Result" + obj; //"Result3" 左边是字符串这样会理解为字符串拼接那么他就会调用obj.toString所以返回结果"Result3"

obj; //3 用一员加号操作符是可以把对象尝试转换为数字的,如果定义了obj.toString也会去调用obj.toString返回3

//valueOf是尝试把对象转换为基本类型的时候会自动去调用的一个函数,这里自定义返回值
obj.valueOf = function() {
return this.x + this.y + 100;
} + obj; //103 再去用一员加号操作符把他转换为数字这次返回的结果是103是从这个valueOf而来的
"Result" + obj; //"Result103"

需要主要的是这里面valueOf和toString都存在的时候那么不管是一元的加号还是2元的字符串拼接在做具体操作时,都会尝试把对象转换为基本类型,那么他会先去找valueOf。如果valueOf返回的是基本类型那么就以valueOf的值作为结果反之如果valueOf不存在或者返回的是对象,那么就会去找toString。如果valueOf和toString都没有或者都返回的对象,那么就会报错。