我正在参与掘金创作者训练营第5期, 点击了解活动详情
什么是原型?
此文章简单记录个人对原型的理解,如果错误,欢迎批评指正
简单类型和复杂类型存储方式的区别
简单类型和复杂类型
javascript中数据类型可以大致分为简单类型(值类型)和复杂类型(引用类型)
简单类型(值类型)包含:boolean,string,nimber,undefined,null
复杂类型包含:除了简单类型都是复杂类型,代表有 Object,Array,Date,function
其中声明值类型和引用类型都需要用到let关键字
let name = '芒果';
function eat(){
console.log('吃肉了');
}
两者的存储方式
其中简单类型变量名和值都存储在栈空间里面
复杂类型将变量名存储在栈空间,在栈空间中,值存储在堆空间,而变量名存储的是复杂类型在堆空间中的地址
赋值操作
简单类型赋值
简单类型我们知道是存储在栈空间,我们将简单类型的数据变量赋值给另一个变量,其实是将这个变量中的值拷贝了一份给另一个变量,那么我们修改另一个变量,对我们原来的变量值没有任何影响
let name = '芒果';
let userName= name;
console.log(userName); //芒果
//此时我们更改userName的值
userName = '苹果';
console.log(userName); //苹果
console.log(name); //芒果
//对我们原理的name没有任何影响
复杂类型赋值
复杂类型变量存储在栈空间,保存的是指向堆空间中的地址值
//我们先定义一个对象
let obj = {
name:'芒果',
age:22
}
//此时我们将obj赋值给obj2
let obj2 = obj;
//此时我们修改obj2中的属性值
obj2.name = '苹果';
//打印obj和obj2
console.log(obj);
/*
age: 22
name: "苹果"
*/
console.log(obj2);
/*
age: 22
name: "苹果"
*/
我们可以看到,其中obj的属性值也发生了改变
这是因为复杂类型在进行赋值时其实是将栈空间中变量中保存的地址值进行赋值,那么obj和obj2都指向同一个堆中的地址,所以修改的是同一个对象,那么修改obj和obj2其中任意一个,都会影响另一个
简单总结:
简单类型的赋值是对值进行拷贝
复杂类型的赋值是地址值的传递
创建对象的一些问题
如何创建对象
首先,我们先思考一个问题,我们想要创建一个对象,可以怎么创建
我们可以使用let声明一个变量,将对象赋值给给这个变量
let person = {
name:'芒果',
age:18,
}
//在创建一个对象person2
let person2 = {
name:'苹果',
age:18,
}
创建多个同类型的对象(工厂函数)
那么我们每次想要创建类似的对象都需要一个一个声明,写这么一大段代码么,有没有可以更为简单的方式去创建大量的同类型的对象
使用工厂函数可以解决这个问题
//我们先定义一个函数
function createPerson(name,age){
const obj = {}
obj.name = name
obj.age = age
return obj
}
let person1 = createPerson('芒果',18)
console.log(person1)
let person2 = createPerson('苹果',18)
console.log(person2)
这样我们可以调用createPerson这个函数更为方便的创建对象
可这样还是觉得有点繁琐
那么我们可以使用new来创建进行创建
function createPerson(name,age){
this.name = name
this.age = age
}
let person1 = new createPerson('芒果',18)
console.log(person1)
let person2 = new createPerson('苹果',18)
console.log(person2)
这里我们也要知道一个名词 构造函数
能够使用函数创建出对象的函数我们成为构造函数 (上述函数createPerson就是构造函数)
上述能够n2 和上一个创建方法得到的是一样的
为什么可以这样写呢,这都是new的功劳
new的工作原理
- 创建一个空对象
- this(调用者,谁用就是谁)用来指向这个对象
- 给这个对象赋值
- 返回这个对象
这里我们也要知道一个名词 实例对象
我们将new出来的对象叫做 实例对象
对象方法的资源浪费问题
我们知道了如何用new来创建对象,那么我们先来创建一个对象
function Person(name,age){
this.name = name
this.age = age
this.eat = function(){
console.log('我喜欢吃肉')
}
}
//我们用new来创建两个对象
person1 = new Person('芒果',18);
person2 = new Person('苹果',18);
//我们比较一下两个对象中的方法
console.log(person1.eat ===person2.eat );//false
我们比较两个对象中的方法显示结果是false,说明我们的方法不是同一个,而这两个方法没有差异啊,那当我们创建大量的对象时,同样的方法却需要开辟很多空间,造成了资源的浪费,我们需要想办法来解决它
先前的解决办法
我们先前是怎样解决的呢?
//1.声明一个对象作为全局变量来保存这些方法
let obj = {
eat: function() {
console.log("我喜欢干饭")
},
learn: function() {
console.log("学习使我感到快乐")
}
}
//2.将创建的对象的方法指向这个对象中的方法
function Person(name, age) {
this.name = name
this.age = age
this.eat = obj.eat
this.learn = obj.learn
}
//3.创建对象
person1 = new Person('芒果',18);
person2 = new Person('苹果',18);
这样的确解决了资源浪费 ,但造成了新的问题,全局变量污染环境啊,我们在开发中万一团队中其他人也声明了一个obj的变量,那么就会造成覆盖,直接报废...
这个时候原型对象就闪亮登场了
原型对象
- 原型对象是什么?
其实每一个函数在声明时系统都会自动创建一个原型对象
prototype我们称为原型或原型对象而我们一般只在
构造函数中才会去使用它
- 原型对象作用?
解决构造函数 内存浪费 + 变量污染
原型对象相关三个属性
- prototype : 属于构造函数,指向原型对象
- proto : 属性实例对象,指向原型对象
- constructor:属于原型对象,指向构造函数
prototype属性
prototype : 属于构造函数,指向原型对象
为什么说原型对象能够解决内存浪费和变量污染呢?
我们先来使用原型对象看一下
function Person(name,age){
this.name = name
this.age = age
}
//2.原型对象
console.log( Person.prototype );
//给原型添加方法
Person.prototype.eat = function(){
console.log('我喜欢吃肉');
}
Person.prototype.learn = function(){
console.log('学习使我快乐');
}
//3.实例对象 : new创建的对象
person1 = new Person('芒果',18);
person2 = new Person('苹果',18);
console.log(p1.eat===p2.eat); //true
可以看到两个实例对象中的方法eat是同一个
这是因为原型对象的本质是一个对象,我们给原型对象添加方法,它在堆空间开辟了固定的空间来存储方法,这样我们使用构造函数创建对象时,实例对象使用eat方法其实是在使用原型对象中的方法
proto 属性
proto 属性是实例对象的属性,它指向了原型对象
它的作用就是直接访问原型对象中的成员,这也是为什么实例对象可以使用原型对象中方法的原因
//上边代码中console.log(p1.eat===p2.eat);
//其实也就是 console.log(p1.__proto__.eat===p2.__proto__.eat);
在实际开发中, proto 我们一般省略不写,因为它不符合W3C的标准
constructor
constructor:属于原型对象,指向构造函数
//在上述代码中,我们可以打印一下p1的 __proto__.constructor的值
console.log( p1.__proto__.constructor )//Person
/*
打印出来的是
ƒ Person(name,age){
this.name = name
this.age = age
}
*/
由此可见,constructor属性指向构造函数,我们可以使用它来看自己的构造函数
静态成员和实例成员
静态成员: 函数的属性
实例成员: 实例对象的属性
function Person(name,age){
this.name = name
this.age = age
}
//给函数添加的属性叫做 静态属性
Person.h = 666
let p1 = new Person('张三',20);
//p1.name就是实例属性
Object中的keys和values方法
把对象所有的'属性值'放入数组中: Object.values(对象名)
把对象所有的'属性名'放入数组中: Object.keys(对象名)
let person = {
name: "张三",
age: 20,
sex: "男"
}
/*js中常用的静态方法
把对象所有的'属性值'放入数组中: Object.values(对象名)
把对象所有的'属性名'放入数组中: Object.keys(对象名)
*/
let arr = Object.values(person)
console.log(arr)//['张三', 20, '男']
let arr1 = Object.keys(person)
console.log(arr1)//['name', 'age', 'sex']