一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情。
JS中的对象
前言:本来是想分享一下,我对原型以及原型链的一些认识或者说看法。但是我个人认为,引出原型的使用场景离不开对象,包括对象的继承模式中也会涉及到原型。所以这篇帖子先分享一些对象相关的非常简单的内容。算是水一水~
1.相关概念
在JS中,对象是一组无序的相关属性和方法的集合,所有的事物都是对象。对象是由属性和方法组成的。 属性:事物的特征,在对象中用属性来表示
方法:事物的行为,在对象中用方法来表示
1.1 变量、属性、方法、函数之间的区别
在知道了对象是什么以及对象是由什么组成的之后,难免会把对象的属性和方法与变量和函数联系到一起。那他们之间的区别是什么?
- 变量与属性相似,都是用来存数据的
eg:
var num = 10;
var obj = {
age: 10
}
变量单独声明 使用的时候直接写变量名
属性是在对象中存在的,不需要声明,使用的时候是 对象.属性或者对象['属性名']
- 函数和方法的相同点,都是实现某种功能、做某件事
函数单独声明,使用时直接 函数名(); 是单独存在的
方法在对象里面,调用的时候 对象.方法()
2.创建对象
一般用4种方式去创建对象,分别是对象字面量、Object构造函数、工厂模式、自定义构造函数
2.1对象字面量
什么是对象字面量?就是花括号{}里面包含了表达这个具体事务(对象)的属性和方法
示例
// 1.利用字面量创建对象{}
// var obj = {}; 创建了一个空对象
var obj = {
uname: '亚索', // 属性:值
age: 32,
sex: '男',
sayHi: function () { //方法的写法就是 方法名:匿名函数
console.log('哈撒ki!!');
}
}
(1) 对象中的属性和方法,采取键值对的形式。即 属性名:属性值、方法名:匿名函数
(2) 多个属性之间用逗号隔开,如上
调用方式
// (1)调用对象的属性,采取 对象名.属性名
console.log(obj.uname);
// (2)调用属性还有一种方法,对象名['属性名']
console.log(obj['age']);
// (3)调用对象的方法 对象名.方法名()
obj.sayHi();
对象字面量适应场景:起始时对象内部的数据是确定的
存在问题:如果创建多个对象会有代码重复
var p = {
name: 'Tom',
age: 12,
setName: function (name) {
this.name = name;
}
}
var p = { //创建多个对象代码重复
name: 'Bom',
age: 12,
setName: function (name) {
this.name = name;
}
}
2.2 Object构造函数模式
就是先创建空Object对象,再动态添加属性和方法
示例
// 一个人:name:"Tom",age:12
var p = new Object();
// 向空对象追加属性和方法
// 利用等号赋值的方法添加属性和方法,每个属性和方法之间用分号结束
p.name = "Tom";
p.age = 12;
p.setName = function (name) {
this.name = name;
}
注意:
//属性名包含特殊字符,如 - 或者空格的时候,这样就追加不进去
// p.content-type = 'ni hao';
//这个时候就可以用['属性名']来追加
p['content-type'] = 'hello world';
console.log(p['content-type']); //'hello world'
//或者属性名不确定的时候,也可以用['属性名']这种方法追加
var propname = 'myAge';
var value = 18;
p.propname = value; //实际上追加进去的属性是 propname,而不是myAge
p[propname] = value; //这样追加进去的,才是myAge
适用场景:起始时不确定对象内部数据
存在问题:语句太多
2.3 工厂模式
什么是
工厂模式?就是通过工厂函数动态创建对象并返回。
什么又是工厂函数? 返回一个对象的函数都可以称为工厂函数
示例
function createPerson(name, age) { //返回一个对象的函数都可以称为工厂函数
var obj = {
name: name,
age: age,
setName: function (name) {
this.name = name;
}
}
return obj;
}
//创建两个人
var p1 = createPerson('tom', 12);
var p2 = createPerson('Bob', 12);
//这种模式就解决上一种方式(字面量模式)代码大量重复的问题
function createStudent(name, price) {
var obj = {
name: name,
price: price
}
return obj;
}
var s1 = createStudent('李四', 12000);
console.log(typeof p1, typeof p2, typeof s1);
//这种模式的问题就是创建的对象没有具体的类型,都是object
适用场景:需要创建多个对象
存在问题:对象没有一个具体的类型,都是Object类型
2.4 自定义构造函数模式
我们为什么需要使用构造函数
就是因为我们前面两种方法一次只能创建一个对象,
一次创建一个对象,里面很多的属性和方法是大量重复的,只能复制
因此可以利用构造函数的方法 重复这些相同的代码 这个函数称为构造函数
示例
// 构造函数的语法格式:
// function 构造函数名() {
// this.属性 = 值;
// this.方法 = function () {};
// }
// 使用时: new 构造函数名();
function Star(uname, age, sex) {
this.name = uname;
this.age = age;
this.sex = sex;
this.sing = function (sang) {
console.log(sang);
}
}
var zxc = new Star('周星驰', 48, '男');
console.log(typeof zxc); // object
console.log(zxc.name);
console.log(zxc['age']);
console.log(zxc.sex);
zxc.sing('唐伯虎'); //以这个例子来说实参传递给构造函数中方法中的形参sang
var zxy = new Star('张学友', 19, '男');
console.log(zxy.name);
console.log(zxy.age);
console.log(zxy['sex']);
zxy.sing('李香兰');
特点:
- 构造函数名字的首字母大写
- 构造函数不需要return就能返回结果
- 我们调用构造函数,必须使用new
- 构造函数中的属性和方法前面必须添加this
使用场景:需要创建多个类型确定的对象
存在问题:每个对象都有相同的数据,浪费内存
3. new关键字执行过程
- new构造函数时可以在内存中创建了一个空的对象
- this 就会指向刚才创建的对象
- 然后执行构造函数里面的代码 给这个空对象添加属性和方法
- 返回这个对象(这也就是构造函数不需要return的原因,因为编译器读到new时,就会执行上面一系列操作,最后返回这个对象)
4. 遍历对象的方法
示例
//遍历对象
var obj = {
uname: 'Guguuuu',
age: 19,
sex: '男',
fn: function () {}
}
// 对象中是没有序号的,不能用普通的for来遍历
// 利用for in 来遍历对象
// for(变量 in 对象){}
for (var key in obj) {
console.log(key); //直接这样输出的是对象的属性名
console.log(obj[key]); //这样才能输出属性值
}
// 注意:for in 中的变量通常我们喜欢写k或者key
5. 相关问题之什么是实例?
之前看弹幕上有人问到了,什么是实例? 书面语中,实例是实际的例子。
实例,就是一个类的真实对象(或者说一个具体的例子)。
比如我们都是“人”,你和我都是“人”类的实例了。
而实例化就是创建对象的过程,用New关键字创建。(创建对象的过程叫实例化)
在计算机语言中,“类”在实例化之后叫做一个“实例”。 “类”是静态的,不占进程内存,而“实例”拥有动态内存。在数据库中,代表一些程序的集合。如Oracle中,实例就是一些能支撑数据库运行的数据库程序。
function Cake(color, material) {
this.color = color;
this.material = material;
}
var obj = {
a: 1,
b: 2
};
var cake1 = new Cake("red", "egg");
var cake2 = new Cake("yellow", "apple");
JS 是基于原型的面向对象语言, 所有数据都可以当作对象处理
比如说蛋糕,Cake就是构造函数,可以理解为做蛋糕的模具。cake1 和cake2,这两个都是实例。
实例简单的理解就是成品。(就像java黑皮书里面讲的,苹果派和苹果派配方之间的关系)
实例和对象的区别,从定义上来讲:
- 实例是类的具象化产品。
- 而对象是一个具有多种属性的内容结构。
- 实例都是对象,而对象不全是实例。
- Js里面没有类(ES6以前)的语法,所以类的概念就通过创造一个对象来实现。
6. 自定义构造函数存在的问题 (引出原型)
还是刚刚那个对应的例子
function Star(uname, age, sex) {
this.name = uname;
this.age = age;
this.sex = sex;
this.sing = function (sang) {
console.log(sang);
}
}
我们创建了一个Star构造函数在这个Star构造函数中,为每一个对象都添加sing方法
这个方法是我们在构造函数内部创建的,也就是说构造函数每执行一次,就会创建一个新的sing方法,所有的实例的sing方法都是唯一的
可以通过console.log(对象1.方法 == 对象2.方法) (方法不加括号) 来证明是否是同一个方法
这就导致了,如果我创建10000个对象,就会创建10000个新的方法,而10000个方法实现的功能是一样的
这是完全没有必要的,完全可以使所有对象共享同一个方法
(就比如一个宿舍10人,给每一个人配一个厕所是非常 ** 的事情,应该是10人共享一个厕所)
解决方法:
解决方法就是把这个 构造函数中这个方法在全局作用域中定义
function fun(sang) {
console.log(sang);
}
再把fun赋值给this.sing
this.sing = fun;
这样即使构造函数执行了1000次,我的sing方法还是只有一个,就是这个fun
但是呢!这么写会有一个问题啊,就是把这个函数定义在全局作用域下,污染了全局作用域的命名空间
我这个函数叫fun,那别人就不能叫fun了,别人要是也叫fun,就把你这个函数覆盖掉了,就是说定义在全局作用域中是很不安全的
接下来就涉及到一个概念,叫原型(prototype)
我打算分开来讲,明天更新了再上链接~
传送入口
其他内容比如对象的继承模式我会在原型那边说明
以上,如果有讲错的地方,请指点指点,谢谢😄~