JavaScript六种比较常见的原型创建对象的方法
程序员文章站
2022-06-12 21:39:31
...
1.传统原型法:
a. 当完成一个函数声明之后, js引擎会自动的创建一个对象, 这个对象表示函数对象.函数对象会有一个属性 prototype, 他指向一个对象, 这个对象也是自动创建出来的, 这个对象
就是函数的原型对象.
原型对象也有一个属性: constructor , 表示构造器,他会指向函数对象.
b. 任何的函数都会有原型对象. 只是, 我们只关注和研究构造函数的原型对象, 普通函数的原型对象
我们不关注.
<script>
function Person(name, age){
this.name = name;
this.age = age;
//如果方法写在函数对象里,那么每次new一个对象就会调用一次方法,在堆内存中创建一个函数对象,很占内存。
/*this.speak = function (){
console.log("我是对象上的方法");
}*/
}
Person.prototype.sex = "男";
//在原型对象上写方法,会比较灵活,而且不占内存。
Person.prototype.speak = function (){
console.log("我是原型对象上的方法");
}
var p1 = new Person("李四", 20);
console.log(p1.age);
console.log(p1.name);
//对象p1没有属性sex,他会通过不可见属性[[ proto]]去访问原型对象里的sex
console.log(p1.sex);
//speak方法写在了原型里,作用是不占过多的内存
p1.speak();
//检测Person.prototype,就是检测其原型对象是谁。在这里原型对象里有speak方法和sex属性。
console.log(Person.prototype);
//判断原型的constructor的指向是不是person,实际上的确,返回true。
console.log(Person.prototype.constructor === Person);
</script>
运行结果:
2.组合模式原型法
所谓组合那就是把属性封装在一起,把所有的方法写在原型里。
a. 数据属性, 应该放在对象本身. 在构造函数中使用 this.xxx = XXX
b. 方法应该添加在原型对象上. 因为方法最好所有的对象所共有.
这种封装性比较差,相对无言。
而且这种的话,每次调用会产生垃圾,比如三次调用同一原型中的同一方法,那么就会两次覆盖其原型的属性,产生了两次的垃圾。为了防止垃圾的产生,我们就有了第三种的动态原型。先看这种组合模式原型:
<script>
// 组合模型创建对象的方式
function Person(name, age){
this.name = name;
this.age = age;
}
//将所有方法写在原型对象里
Person.prototype.speak = function (){
console.log("speak..." + this.name);
}
Person.prototype.say = function (){
console.log("say..." + this.age);
}
Person.prototype.eat = function (){
console.log("eat....");
}
var p1 = new Person("李四", 20);
var p2 = new Person("李三", 200);
var p3 = new Person("李二", 120);
p1.speak();
p2.speak();
p3.speak();
</script>
运行结果:
3.动态原型
通过if判断,看他是否已经产生了。这样就不会产生垃圾。
这个封装性挺好,因为放在整个对象内。
<script>
// 动态原型创建对象
function Person(name, age){
this.name = name;
this.age = age;
if (!Person.prototype.speak){
//看他里面的方法执行了几次调用了几次
console.log("我内容只有一次,那么就说明我这个speak方法在原型中只创建了一次。");
Person.prototype.speak = function (){
console.log("speak..." + this.name);
}
}
/*Person.prototype.say = function (){
console.log("say..." + this.age);
}
Person.prototype.eat = function (){
console.log("eat....");
}*/
}
var p1 = new Person("李一", 10);
var p2 = new Person("李二", 20);
var p3 = new Person("李三", 30);
//检测函数对象是否有speak方法,有的话就输出函数,没有就输出undefined。
//输出undefined是JS的一种安全机制,如果对象.属性(方法),如果这个方法或属性没有定义声明,那么只是显示undefined
//但如果是单纯的变量,那么如果没有声明定义,就会报错,报错信息为:XX没有被定义。
Person.prototype.speak();
p1.speak();
p2.speak();
p3.speak();
</script>
运行结果:
4.原型替换一
为啥要改变系统的默认原型:
本来我们为原型里添加属性方法要这样做:
Person.prototype.speak = function (){
console.log("speak..." + this.name);
}
但是我们现在如果给一个自己设定的原型,我们在自己创建对象里添加方法和属性只需:
speak: function (){
console.log(this.name);
},
是不是更简单点更方便点呢。
456三种原型替换的方法,都是这么做,只是依次还会有些改进。
<script>
function Person(name){
this.name = name;
}
//改变系统的指向到那个原型,我们另外创建一个对象,让person函数对象指向我们新创建的对象。这里用对象字面量的表达方式。
Person.prototype = {
// 让他指向回去
constructor: Person,
speak: function (){
console.log(this.name);
},
say: function (){
}
}
var p1 = new Person("李四");
p1.speak();
</script>
运行结果:
5.原型替换二
把数据属性通过参数封装起来。
<script>
//把一些数据属性也封装起来,通过参数传递
function Person(name, age, sex){
this._init(name, age, sex);
}
//改变系统默认原型指向
Person.prototype = {
//初始化数据属性值
_init :function (name, age, sex){
this.name = name;
this.age = age;
this.sex = sex;
},
//指向回去
constructor: Person,
speak: function (){
console.log(this.name);
},
say: function (){
}
}
var p1 = new Person("李四");
p1.speak();
</script>
运行结果:
5.终极原型替换法(大神级别)
这种写法就是大神级别的啦。
<script>
//把一些数据属性也封装起来,通过参数传递
function Person(name, age, sex){
this._init(name, age, sex);
}
//改变系统默认原型指向
Person.prototype = {
//初始化数据属性值
_init :function (name, age, sex){
this.name = name;
this.age = age;
this.sex = sex;
},
//指向回去
constructor: Person,
speak: function (){
console.log(this.name);
},
say: function (){
}
}
var p1 = new Person("李四");
p1.speak();
</script>
运行结果:
再来我们看一下用这种大神级别的方法进行练习:
题目:题目: 定义一个创建“长方形”的构造函数(有参数,通过参数给属性赋值) 属性:长(length);(实例属性) 宽(width);(实例属性) 方法:求面积;(原型方法) 求周长;(原型方法) 通过这个构造函数创建出3个不同的对象,并调用对象方法计算对象的面积和周长;
solution:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Rectangle</title>
</head>
<body>
<!--题目: 定义一个创建“长方形”的构造函数(有参数,通过参数给属性赋值)
属性:长(length);(实例属性)
宽(width);(实例属性)
方法:求面积;(原型方法)
求周长;(原型方法)
通过这个构造函数创建出3个不同的对象,并调用对象方法计算对象的面积和周长;
-->
<script>
//构造函数,传递一个对象obj
function Rectangle(obj) {
this._init(obj);
}
//原型替换
Rectangle.prototype = {
//使得原型函数又指向Rectangle函数对象
constructor:Rectangle,
//原型的方法,对初始化属性进行单独封装成对象,用对象传递
_init: function (obj) {
//css6中的方法,就是把obj对象中的属性全部传到this里
Object.assign(this, obj);
},
area: function () {
return this.width * this.length;
},
perimeter: function () {
return this.width * 2 + this.length * 2;
}
};
//new对象
var r1 = new Rectangle({length: 10, width: 10});
var r2 = new Rectangle({length: 20, width: 90});
var r3 = new Rectangle({length: 50, width: 60});
//output information
console.log("r1长方形面积为: " + r1.area());
console.log("r1长方形周长为: " + r1.perimeter());
console.log("r2长方形面积为: " + r2.area());
console.log("r2长方形周长为: " + r2.perimeter());
console.log("r3长方形面积为: " + r3.area());
console.log("r3长方形周长为: " + r3.perimeter());
</script>
</body>
</html>
上一篇: 用ES6编写高阶箭头函数
下一篇: 跑步健身养生 避开两时间段