欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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>
运行结果:

JavaScript六种比较常见的原型创建对象的方法

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>

运行结果:

JavaScript六种比较常见的原型创建对象的方法


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>
运行结果:

JavaScript六种比较常见的原型创建对象的方法

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>

运行结果:

JavaScript六种比较常见的原型创建对象的方法


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>

运行结果:

JavaScript六种比较常见的原型创建对象的方法

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>

运行结果:

JavaScript六种比较常见的原型创建对象的方法

再来我们看一下用这种大神级别的方法进行练习:

题目:
题目: 定义一个创建长方形的构造函数(有参数,通过参数给属性赋值)
属性:长(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>


相关标签: 原型