说明
对象API的比较在于:
-
是Obejct静态方法、或通过实例调用原型方法
-
是否能够取到原型的属性
-
是否能够检索非枚举属性
-
是否改变原有对象
-
参数、返回值
ECMAScript 中有两种对象属性:数据属性和访问器属性。
对象中属性
-
数据属性
-
[[Configurable]]
-
不能删除该属性,也不是重新define该属性
-
[[Enumerable]]
-
[[Writable]]
-
[[Value]]
-
访问器属性
-
[[Configurable]]
-
[[Enumerable]]
-
[[Set]]
-
[[Get]]
var person = {}; Object.defineProperty(person, "name", { configurable: false, value: "Nicholas" }); alert(person.name); //"Nicholas" delete person.name; alert(person.name); //"Nicholas"
// get、set与value是互斥的 var book = { _year: 2004, edition: 1 }; Object.defineProperty(book, "year", { get: function(){ return this._year; }, set: function(newValue){ if (newValue > 2004) { this._year = newValue; this.edition += newValue - 2004; } } });
对象的可枚举性
添加一个对象的属性默认是可枚举的。enumerable
概念
可枚举性(enumerable)用来控制所描述的属性,是否将被包括在for...in循环之中。具体来说,如果一个属性的enumerable为false,下面三个操作不会取到该属性。
-
for..in循环
-
Object.keys()方法
-
JSON.stringify()方法
obj.getOwnPropertyNames();可以取到非枚举值。
enumerable “隐身术”
var o = {a:1, b:2};
o.c = 3;
Object.defineProperty(o, 'd', {
value: 4,
enumerable: false
});
o.d
// 4
for( var key in o ) console.log( o[key] );
// 1
// 2
// 3
console.log(Object.keys(o)); // ["a", "b", "c"]
console.log(JSON.stringify(o)); // "{a:1,b:2,c:3}"
console.log(Object.getOwnPropertyNames(o)); // ['a', 'b', 'c', 'd']
上面代码中,d属性的enumerable为false,所以一般的遍历操作都无法获取该属性,使得它有点像“秘密”属性,但还是可以直接获取它的值。
至于for...in循环和Object.keys方法的区别,在于前者包括对象继承自原型对象的属性,而后者只包括对象本身的属性。如果需要获取对象自身的所有属性,不管enumerable的值,可以使用Object.getOwnPropertyNames方法。
当然啦,for in获取原型属性也不会包括js内置的对象属性,只是用户自定义的。
默认不可访问原型上的属性和方法包括:hasOwnProperty()、 propertyIsEnumberable()、toString() 、valueOf()
如何设置enumerable
// 方式1. 创建对象后 添加属性
var o = {a:1, b:2};
Object.defineProperty(o, 'd', {
value: 4,
enumerable: false
});
// 方式2. 在创建对象时就定义
var my_obj = Object.create({}, {
getFoo: {
value: function() { return this.foo; },
enumerable: false
}
});
访问、添加、修改
var obj = {
sex : "不详",
socre : 100,
flag : true,
sing : function{
console.log("爱唱歌")
},
play : function{
console.log("打游戏")
}
}
对象的操作:
访问值:(查)
console.log(obj.sex);
console.log(obj["socre"]);
console.log(obj.sing());
console.log(obj['sing']());
添加一个属性:(增)
obj.hobby = "睡觉";
obj["weight"] = “50kg”;
obj.dance = function(){}
修改:(改)
obj.sex = “女”;
obj["socre"] = 99;
删除:(删)
delete obj.flag;
delete obj["sex"];
注意:delete删除对象的某个属性时就删除了,
但是在删除数组中某个元素时会留下空位
访问属性的时候,可以用obj.属性名或者obj["属性名"];需要执行方法的时候,需要用“.”的方式去调用。
请避免将字符串、数值或布尔声明为对象。他们会增加代码的复杂性并降低执行速度。
定义一个私有属性
1.闭包
2.构造函数:定义局部变量,get和set方法暴露出来
3.class中
class Person {
constructor(name, age) {
// 私有属性
let _name = name;
this.age = age;
this.setName = function (name) {
_name = name;
};
this.getName = function () {
return _name;
};
}
}
循环遍历对象
for in
遍历出来的是属性名
// eg:1
var obj = {
name : 'wanghang',
age : 18,
sex : 'males',
load:function(){
console.log('do');
}
}
for(let prop in obj){
//prop对应 obj中的”name”,”age”,”sex”
//因为底层原理obj.prop --->obj[‘prop’]所以遍历之后没有结果
// console.log(obj.prop);// 错误遍历方式
console.log(typeof prop); // string
console.log(obj[prop]);//正确遍历方式
}
// eg:2
var obj = {a:1, b:2, c:3};
for (var prop in obj) {
console.log("obj." + prop + " = " + obj[prop]);
}
// Output:
// "obj.a = 1"
// "obj.b = 2"
// "obj.c = 3"
官网:for...in不应该用于迭代一个关注索引顺序的 [Array](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Array)。
一般说for...in 就不用于数组
估计是遍历时顺序不能保证?不是这么说的
Array.prototype.eee = 'eee'
var arr = ['aaa','bbb','ccc','ddd']
console.log(arr)
for (var i in arr){
console.log(arr[i])
}
// 因为也会遍历出Array原型对象上人为定义的属性,
// 有一些库会去增加Array上的原型属性的,这样把这些属性也遍历出来就好了嘛
for-in循环会遍历实例和原型链上可枚举的所有属性,屏蔽了所有不可枚举属性。
Object.keys()
作用:用于返回对象可枚举的、包括实例不包括原型的属性名称数组。
参数:【Object】 必传
var a = {name : 'kong', age : 18, func : function(){}};
Object.keys(a); //['name', 'age', 'func']
与Obejct.getOwnPropertyNames()的区别:
都是只能获得自己实例的属性,而不能获得原型上的属性。
Object.keys():不能获取对象的不可枚举属性。
Object.getOwnPropertyNames():能获得对象的不可枚举属性。
Object.getOwnPropertyNames()
官方:
**Object.getOwnPropertyNames()**方法返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括Symbol值作为名称的属性)组成的数组。
df:得到对象的key的数组。也不包括原型上的key
参数:【Object】 一个对象
对象遍历方法比较
是否能作用原型上的属性
是否能作用非枚举的属性
for in
是
否
Object.keys()
否
否
Object.getOwnPorpertyNames()
否
是
object.hasOwnProperty()
否
是
in
这也说明了:没有啥办法能够遍历出原型上的对象内置方法,没有两个都‘是’的方法。
总结js中带in的forin和in,都可以访问原型属性。带property的api都可以访问非枚举属性。
对象属性方法
Object.is()
作用:用于判断两个对象是否相同
参数:第一个 【Any】
第二个 【Any】
返回:【Boolean】
Object.is(a, b);//返回true或false
//注意,该函数与==运算符不同,不会强制转换任何类型,
应该更加类似于===,但值得注意的是它会将+0和-0视作不同值
1.+0与-0
console.log( 0 === -0); // true
console.log(Object.is(0,-0)); // false
2.NaN
console.log( NaN === NaN); // false
console.log(Object.is(NaN,NaN)); // true
console.log(Object.is(0/0,NaN)); // true
Object.create(proto,[propertiesObject])
Object.create()方法创建一个新对象,使用参数对象来提供新创建的对象的__proto__。
其实这不是克隆作用,到像是继承。
返回:新的【Object】
var Plane = function(){
this.blood = 100;
this.attackLevel = 1;
this.defenseLevel = 1;
};
var plane = new Plane();
var clonePlane = Object.create( plane );
console.log( clonePlane ); // 输出:Object {blood: 500, attackLevel: 10, defenseLevel: 7}
//在不支持 Object.create 方法的浏览器中,则可以使用以下代码:
Object.create = Object.create || function( obj ){
var F = function(){};
F.prototype = obj;
return new F();
}
Object.assign()
es6新增
用于合并对象,给目标对象添加另一个对象的全部属性
参数:
第一个参数:【Object】 结果对象
第二个参数: 【Object】 目标对象
。。。:【Object】 目标对象
返回:第一个参数的引用,就是修改后的第一个参数对象的引用
var first = {name : 'kong'};
var last = {age : 18};
var person = Object.assign(first, last);
console.log(person);//{name : 'kong', age : 18}
console.log(first);//{ name: 'kong', age: 18 }
console.log(last);//{ age: 18 }
console.log(person === first); // true
案例:
Object.assign 方法可以很方便地一次向类添加多个方法。
class Point {
constructor(){
// ...
} }
Object.assign(Point.prototype, {
toString(){},
toValue(){}
}
);
Object.defineProperty()
作用:劫持变量的set和get方法,将属性添加到对象,或修改目标属性的特性。修改属性的特性的。
返回:并返回此对象,或则一个{}空对象。
参数:
1.【Object】目标对象
2.【String】key的字符串,目标对象的属性名
3.【Object】get、set方法、value、enumerable
注意:千万不能递归获取和设置。
这个api是唯一能让delete删除对象属性返回为false的;
案例1:
var a = {
name:'dingfeng'
};
console.log(a.name); // dingfeng
Object.defineProperty(a, 'name', {
value : 'kong',
enumerable : true //该属性是否可枚举
})
console.log(a.name); // kong
案例2:
const data = {};
let name = "张三";
Object.defineProperty(data,'name',{
get:function(){
console.log('触发get')
return name
// return data.name // 递归导致内存泄漏
},
set:function(newVal){
console.log('触发set')
name=newVal
// data.name = newVal //递归导致内存泄漏
}
})
//测试
console.log(data.name) // 触发get 张三
data.name = '李四' // 触发set
案例3
const data = {
person:[]
};
let obj = Object.defineProperty(data,'person',{
get:()=>{
console.log('get');
return {}
},
set:()=>{
console.log('set');
},
})
data.person.push(1); // 报错:没有这个方法。可见vue中并不是。
解决递归设置获取操作时内存泄露问题:使用函数this对象 or 使用函数闭包特性
/**
* 方式一:使用函数this对象
**/
let data = {
name:'尚硅谷',
address:'北京',
}
//创建一个监视的实例对象,用于监视data中属性的变化
const obs = new Observer(data)
console.log(obs)
//模拟一个vm实例对象
let vm = {}
vm._data = data = obs
function Observer(obj){
//汇总对象中所有的属性形成一个数组
const keys = Object.keys(obj)
//遍历
keys.forEach((k)=>{
// dingfeng: 这个this玩的很6啊
Object.defineProperty(this,k,{
get(){
return obj[k]
},
set(val){
console.log(`${k}被改了,我要去解析模板,生成虚拟DOM.....我要开始忙了`)
obj[k] = val
}
})
})
}
/**
方式二:利用函数闭包
*/
let obj = {
name: 123,
};
function defineReactive(data, key, val) {
Object.defineProperty(data, key, {
get() {
console.log('你试图访问obj的' + key + '属性');
return val;
},
set(newValue) {
console.log('你试图修改obj的' + key + '属性', newValue);
if (val === newValue) {
return;
}
val = newValue;
},
});
}
defineReactive(obj, 'name', obj.name);
console.log(obj);
console.log(obj.name);
obj.name = '张三';
console.log(obj);
注意:该api还有一个特性,比如获取obj.a.m.n 时每次"点"都会触发getter函数
Object.defineProperties()
…可添加多个属性,与Object.defineProperty()对应,
Object.defineProperties(a, {
name : {
value : 'kong',
enumerable : true
},
job : {
value : 'student',
enumerable : true
}
})
Obj.prototype.isPrototypeof()
确定一个对象是否存在于另一个对象的原型链中
function A(){}
var a = new A();
console.log(A.prototype.isPrototypeOf(a));//true
console.log( a instanceof A); // true
Object.getPrototypeOf()
用来得到原型对象
同来替代__proto__
解决__proto__是不规范的,是各大浏览器厂商各自添加的
可以用来从子类上获取父类
因此,可以使用这个方法判断,一个类是否继承了另一个类。
Object.getPrototypeOf(ColorPoint) === Point
// true
Object.getOwnPropertyDescriptor()
获取指定属性的属性描述符
class CustomHTMLElement {
constructor(element) {
this.element = element;
}
get html() {
return this.element.innerHTML;
}
set html(value) {
this.element.innerHTML = value;
} }
var descriptor = Object.getOwnPropertyDescriptor(
CustomHTMLElement.prototype, "html"
);
"get" in descriptor // true
"set" in descriptor // true
Object.getOwnPropertyDescriptors()
ES2017新增
Object.getOwnPropertySymbols()
判断对象属性是否存在
in
检查某个键名是否存在的运算符in,适用于对象,也适用于数组。
如果数组的某个位置是空位,in运算符返回false。
但是数组使用的方式不太一样,它是指数组的键名就是索引, 参数String会被转成number,所以在数组中也没啥用
用在对象时,注意是string类型哦,写数字的时候会自动转成string的,只是感觉不出来
const a = {
hello:'1213'
}
console.log('hello' in a) // true
const b = ['12','123','34']
console.log(1 in b) // true
console.log('1' in b) // true
console.log('123' in b) // false
每当代码读取某个对象的某个属性时,都会执行一次搜索,目标是具有给定名字的属性。搜索首先从对象实例本身开始。如果在实例中找到了具有给定名字的属性,则返回该属性的值;如果没有找到,则继续搜索指针指向的原型对象,在原型对象中查找具有给定名字的属性。如果在原型对象中找到了这个属性,则返回该属性的值。虽然可以通过对象实例访问保存在原型中的值,但却不能通过对象实例重写原型中的值。如果在实例中添加一个与原型中属性同名的属性,则该属性会屏蔽原型中的那个属性。添加的同名属性只会阻止我们访问原型中的那个属性,但不会修改那个属性。即使将这个属性设置为null,也只会在实例中设置这个属性,而不会恢复其指向原型的链接。
in操作符只要通过对象能访问到属性就返回true。会去找原型中的属性。
hasOwnProperty()只在属性存在于实例中时才返回true。不会去找原型的属性。
使用delete操作符则可以完全删除对象实例属性,从而让我们能巩固重新访问原型中的属性,不会删除原型属性。
// 好好看,这段代码很好
function Person(){
}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
alert(this.name);
};
var person1 = new Person();
var person2 = new Person();
alert(person1.hasOwnProperty("name"));//false
alert("name" in person1);//true
person1.name = "Greg";
alert(person1.name);//"Greg"
alert(person1.hasOwnProperty("name"));//true
alert("name" in person1);//true
delete person1.name; // delete 不会删除原型属性
alert(person1.name);//"Nicholas"
alert(person1.hasOwnProperty("name"));//false
alert("name" in person1);//true
obj.hasOwnProperty()
方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键)。
对象实例调用
参数:【String】【必须】需要查找的键名
返回:【Boolean】
不可获取原型属性,可获取非枚举属性
eslint校验后,需要使用
Object.prototype.hasOwnProperty.call(obj,"name")
因为obj.hasOwnProperty()可能会被一些库重写
// eg1:不可获取原型属性
var triangle = {a: 1, b: 2, c: 3};
function ColoredTriangle() {
this.color = 'red';
}
ColoredTriangle.prototype = triangle;
var obj = new ColoredTriangle();
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
console.log(`obj.${prop} = ${obj[prop]}`);
}
}
// Output:
// "obj.color = red"
//eg2:不可获取原型属性
var a = {name : 'kong', age : 18, func : function(){}};
a.__proto__.gener = '男'
console.log(a.hasOwnProperty('name')); // true
console.log(a.hasOwnProperty('gener')); // false
// eg2:可获取非枚举属性
var a = {name : 'kong', age : 18, func : function(){}};
Object.defineProperty(a, 'gener', {
value: '男',
enumerable: false
});
console.log(a.hasOwnProperty('name')); // true
console.log(a.hasOwnProperty('gener')); // true
===
给对象赋值
返回赋的值
给对象赋值,会返回赋的值,oh太神奇了。
var Days = {};
console.log(Days["Sun"] = 0);
console.log(Days["Sun1"] = 100);
console.log(Days["Sun2"] = 'qwe');
0
100
qwe
对象key的顺序
对象key的顺序能够保持定义时的顺序吗?
答:不一定
规则:正整数会移动到最前,并且按从小到大排列,不管定义时的顺序;其他类型的key会安装定义时的顺序排列。
const obj2 = {
a:null,
c:null,
"-1":null,
b:null,
1:null,
0:null
}
console.log(obj2);
console.log(Object.keys(obj2));
console.log(Object.getOwnPropertyNames(obj2));
for (const key in obj2) {
console.log(key);
}
// 0 1 a c -1 b