作者: 她不美却常驻我心
博客地址: https://blog.csdn.net/qq_39506551
微信公众号:老王的前端分享
每篇文章纯属个人经验观点,如有错误疏漏欢迎指正。转载请附带作者信息及出处。
对象这一概念是初学者比较难以理解的点,理论性的东西较多,而且需要理解的思想也不少,所以初学者可以先将对象理解成一种特殊的数据存储方式,再慢慢理解。
一、什么是对象
对象 Object
是 JS 的一种特殊的基本数据类型。有一句话非常的有名,叫做 万物皆对象 ,也就是说,JS 中的所有事物都是对象,包括字符串、数字等。
var str = "字符串"; // 实际上创建了一个新的字符串对象,继承了 String 对象的属性和方法。
console.log(str.__proto__); // 指向 String 对象
对象 Object
可以看做是拥有属性和方法的数据。我们来举个生活中的例子来详细说明什么是对象:
我们将动物 Animal 看做是一个类,而狗 dog 是属于动物这个类中的一个对象,狗的名字、年龄、性别可以看做是它的属性,而它会跑、会吃东西这些动作行为,可以看做是它的方法。( 类 + 对象 的形式是面向对象编程的基础,初学者可以慢慢理解)
var dog = {
name : "旺财",
age : "3",
sex : "公",
run : function(){
console.log("跑");
},
eat : function(){
console.log("吃");
}
};
我们简单的介绍了一下什么叫做对象,并且通过代码将它的属性和方法描述了出来,对象可以看做是属性方法的集合,通过键值对的方式储存起来,每个键名为这个对象的属性名和方法名。
然而对象并不只是一种数据存储方式,在 JS 中,它是一个极为特殊的存在,因为它除了这些属性外,还从原型对象上继承了一些属性和方法,对象的属性通常都是继承的属性。这种从原型上继承的行为,也是 JS 的核心特征。
Object
同时也是一种复合类型的数据,可以将很多的数据通过键值对的形式表示出来。接下来我们通过对象的方式来对数据进行存储:
var obj = {
author_name : "她不美却常驻我心" ,
Wechar : "老王的前端分享",
"author age" : 18,
"标题" : "从零开始学前端" ,
"if" : "使用关键字命名"
}
键名通常是字符串类型的数据,符合 JS 的命名标准。任何的字符都可以通过增加引号来作为对象的键名,但我们并不推荐使用中文字符、保留关键字等不符合命名规范的特殊的方式进行命名,比如说例子中的"键名"、"if"
。
符合命名规范的字符可以直接作为对象的键名,不需要添加额外的引号,而对象的值可以是任意数据类型。
二、对象的方法
1. 创建对象
- 直接创建
var obj = {};
- 通过构造函数创建
var obj = new Object(); // 等同于 var obj = {};
- 通过对象方法创建
var obj = Object.create();
2. 访问对象
var obj = {
a : 1,
"特殊键名" : 2
}
- 对象名 + “.” + 属性名 (ObjectName.propertyName)
obj.a; // 1
obj.特殊键名 // 2 特殊键名采用这种方式访问在低版本浏览器中会报错
- 对象名 + " [ " + " 特殊属性名" + " ] " (ObjectName[“propertyName”];
obj["a"]; // 1
obj["特殊键名"]; // 2
一般我们采用第一种方式来访问对象的属性,当属性名为中文等特殊情况时,可以使用第二种方式来访问。
3.对象的增删改查
3.1 增加属性
var obj = {};
obj.a = 1;
// 为 Object 添加新方法
Object.prototype.addObject = function (key, value) {
this[key] = value;
return this;
}
obj.addObject("test",3); // {a : 1, test : 2}
3.2 删除属性
var obj = { a : 1 };
delete obj.a;
3.3 修改对象属性
var obj = { a : 1 };
obj.a = 2 ;
// 重新定义已经拥有的方法
Object.prototype.create = function(){
// do something
}
3.4 查找对象属性
var obj = {
a : 1,
"特殊键名" : 2
}
- 使用循环遍历对象:
for (var key in obj) {
console.log(key); // a , 特殊键名
console.log(obj[key]) // 1 , 2
}
// 按照对象属性的顺序依次打印。
- 使用对象内置方法:Object.keys()
var keys = Object.keys(obj);
console.log(keys); ["a" , "特殊键名"];
// 返回一个包括对象内可枚举属性以及方法名称的数组
- 使用对象内置方法:Object.getOwnPropertyNames()
var keys = Object.getOwnPropertyNames(testObj);
console.log(keys);
// 返回一个指定对象所有自身属性的属性名的数组
3.5 检查对象中是否存在属性
- 使用运算符
in
如果对象的属性或者继承属性中
var obj = { a : 1 };
"a" in obj; // true
"b" in obj; // false
"valueOf" in obj // true obj 继承了 Object 对象原型链上的方法。
- 使用内置方法 Object.hasOwnProperty()
obj.hasOwnProperty("a"); // true
obj.hasOwnProperty("valueOf"); // false
- 使用内置方法 Object.propertyIsEnumerable
obj.propertyIsEnumerable("a"); // true
obj.propertyIsEnumerable("valueOf"); // false
4. 内置方法
4.1 Object.assign() 复制对象的可枚举属性
语法:Object.assign(target, source...)
作用:将对象的可枚举属复制到目标对象,并返回目标对象。
实例:
var obj1 = {};
var obj2 = { a: 1, b: 2 };
var obj3 = { a: 2, c: 3 };
var newObj = Object.assign(obj1, obj2,obj3);
console.log(obj1); // {a: 2, b: 2, c: 3}
console.log(obj2); // {a: 1, b: 2}
console.log(obj3); // {a: 2, c: 3}
console.log(newObj); // {a: 2, b: 2, c: 3}
- 如果目标对象中具有相同的属性名称,则后面的源对象的属性将类似地覆盖前面的源对象的属性;
- 该方法实现的是浅拷贝,继承属性和不可枚举属性是不能复制的;
4.2 Object.create() 创建一个新对象
语法:Object.create(proto, {property})
作用:创建一个新对象,使用现有的对象来提供新创建的对象的原型。
实例:
var obj = Object.create(Object.prototype, {
a: {
writable: false, // 设置值是否可更改
configurable: true, // 设置属性是否可配置
enumerable: true, // 设置属性是否可枚举
value: 1 // 值
},
b:{
}
})
obj.a = 2;
console.log(obj); // { a : 1}
4.3 Object.defineProperty() 定义或修改对象属性
语法:Object.defineProperty(obj, key, descriptor)
作用:在一个对象上定义新的属性或修改现有属性,并返回该对象。
实例:
var obj = { a : 1 };
Object.defineProperty(obj, 'b',{
configurable : true ,
enumerable : true ,
writable: true ,
get: function(){},
set: function(){},
value : 2
})
4.4 Object.defineProperties() 定义或修改对象属性
语法:Object.defineProperties(obj, props)
作用:在一个对象上定义新的属性或修改现有属性,并返回该对象。
实例:
var obj = { a: 1 };
Object.defineProperties(obj, {
a: {
value: 2,
writable: false
},
b: {
value: 3,
writable: true
}
});
obj.a = 4 ;
obj.b = 5 ;
console.log(obj); // { a : 2 , b : 5 }
4.5 Object.getOwnPropertyDescriptor()
语法:Object.getOwnPropertyDescriptor(obj, key)
作用:返回对象中属性对应的属性描述。
实例:
var obj = { a: 1 };
var desc = Object.getOwnPropertyDescriptor(obj, "a");
console.log(desc); // {value: 1, writable: true, enumerable: true, configurable: true}
4.6 Object.getOwnPropertyNames()
语法:Object.getOwnPropertyNames(obj)
作用:返回一个包含对象中所有属性的数组。
实例:
var obj = Object.create(Object, {
a: {
value: 1,
enumerable: false
},
b: {
value: 2,
enumerable: true
}
})
var keys = Object.getOwnPropertyNames(obj);
console.log(keys); // ["a", "b"]
4.7 Object.keys()
语法:Object.keys(obj)
作用:返回一个包含对象中所有可枚举属性的数组。
实例:
var obj = Object.create(Object, {
a: {
value: 1,
enumerable: false
},
b: {
value: 2,
enumerable: true
}
})
var keys = Object.keys(obj);
console.log(keys); // ["b"]
4.8 Object.hasOwnProperty()
语法:Object.hasOwnProperty(key)
作用:检测对象中是否含有指定属性。
实例:
var obj = { a : 1 }
obj.hasOwnProperty("a"); // true
obj.hasOwnProperty("b"); // false
4.9 Object.freeze()
语法:Object.freeze(obj)
作用:不允许对对象进行任何修改。
实例:
var obj = { a : 1 }
Object.freeze(obj);
更多关于 Object 的方法及属性,请 点击这里查看。
三、 getter 和 setter
对象的每一个属性都可以看成是由键值对构成的,我们可以使用 getter
和 setter
方法来代替属性值。通过这种方法定义的属性被称为 存取器属性。
var obj = {
_hour_: 0,
get hour() {
return "现在是" + this._hour_ + "点";
},
set hour(h) {
this._hour_ = h < 10 ? '0' + h : '' + h;
}
}
obj.hour = 22;
console.log(obj.hour); // 现在是22点
obj.hour = 9;
console.log(obj.hour); // 现在是09点
当我们访问对象的属性时,会调用 getter
方法,给属性赋值时,会调用 setter
方法。
使用 getter
和 setter
可以在对象的赋值和读取前做一些预处理的工作,比如上方代码在读取和设置时间时对其进行了一些预处理,保证在使用时不需要在进行额外的处理。
当一个属性只存在 getter
方法时,这个属性就是只读属性,不允许对它的值进行修改;如果只存在 setter
方法,则被称为只写属性,读取属性时会返回 undefined
;
四、 原型及原型链
JS 本身并不具有 “类” 的概念,所有的一切都是对象。对象与对象之间的关系是如何联系起来的呢?就是靠着原型链串联在一起的。
每一个对象都具有一个 __proto__
属性,它指向的就是这个对象所继承的原型。而原型也拥有一个 prototype
属性与之相对应,也就是说:当前对象的 __proto__
属性与它继承的原型对象的 prototype
相同。
var str1 = "字符串";
console.log(str1);
console.log(str1.__proto__);
var str2 = new String("字符串");
console.log(str2);
console.log(str2.__proto__);
console.log(str1.__proto__ === String.prototype);
可以发现,我们通过直接赋值的方式创建的是一个普通的字符串类型的数据,而通过构造函数创建的则是一个对象类型的字符串数据,但他们的原型都指向了 String
对象。也就是说,他们都是从 String
这个 “父类” 下的 “子类” ,继承了父类的方法。
接下来我们在打印一下 String
的原型和 Object
的原型:
console.log(str1.__proto__.__proto__);
console.log(Object.prototype);
console.log(str1.__proto__.__proto__ === Object.prototype);
这样的结果已经很明显了,我们声明的普通字符串继承自 String
对象,而 Striing
对象又继承了 Object
对象。这里的指向继承关系,我们就可以将其看做是 JS 的原型链。
JS 的一切都是基于对象进行的设计,要说的内容也太多了点,洋洋洒洒写了半天感觉连个大概也没说明白,内置方法更是一带而过,原型和原型链也只是说了个大概,还有对象的继承等等根本就没有提。这里先立个 flag ,等写完函数的相关内容之后,再来重新写一下对象。(如果没实现的话,我就返回来把这段话删掉。笑)
种一棵树,最好的时间是十年前,其次是现在。人的一生,总的来说就是不断学习的一生。
蚕吐丝,蜂酿蜜。人不学,不如物。与其纠结学不学,学了有没有用,不如学了再说。
每篇文章纯属个人经验观点,如有错误疏漏欢迎指正。转载请附带作者信息及出处。您的评论和关注是我更新的动力!
请大家关注我的微信公众号,我会定期更新前端的相关技术文章,欢迎大家前来讨论学习。
都看到这里了,三连一下呗~~~。点个收藏,少个 Bug 。