JS中的构造函数,原型,原型链,原型链继承

JavaScript是一门基于原型的解释型脚本语言,运行在浏览器端,同时JS也是面向对象的编程语言,在JS中所有的事物都可以看作是对象,字符串,数字,函数等等。
首先复习一下创建对象的方式有哪些?
1.通过对象字面量来创建:var student = { key:value,...}
2.通过new Object()创建对象:

var student = new Object();
  student.name = 'zhangsan',
  student.age = 18,
  student.gender = 'male',
  student.sayHi = function () {
    console.log("hi,my name is "+this.name);
  }

以上两种方式当遇到需要创建多个同类型对象的时候,代码的冗余率太高,需要改进,
改进方式一:工厂函数:

function createStudent(name,age,gender){
	var student = new Object();
	  student.name = 'zhangsan',
	  student.age = 18,
	  student.gender = 'male',
	  student.sayHi = function () {
	    	console.log("hi,my name is "+this.name);
	  }
	  return student;
}

var s1 = createStudent('zhangsan', 18, 'male');
var s2 = createStudent('lisi', 19, 'male');

这样封装代码确实解决了代码冗余的问题,但是每次调用函数 createStudent() 都会创建新函数 sayHi(),也就是说每个对象都有自己的 sayHi() 版本,而事实上,每个对象都共享一个函数。为了解决这个问题,我们引入面向对象编程里的一个重要概念:构造函数。构造函数的函数名是大写的

通过【构造函数】来创建对象:

function Student(name,age,gender){
	this.name = name;
	this.age = age;
	this.gender = gender;
	this.sayhi = function(){
		console.log("hi,my name is" + this.name)
	}

var s1 = new Student("zhansan",18,"male");

}

构造函数和工厂函数的区别:
(1)构造函数内没有创建对象,而是使用this关键字,将属性和方法赋给了this对象;
(2)构造函数内没有 return 语句,this属性默认是构造函数的返回值;
(3)函数名使用的是大写的Student;
(4)用 new 运算符和类名Student 创建对象。

但是构造函数还是存在一些问题,就拿上面的 Student构造函数来说,由于每个对象都是由 new Student 创建出来的,因此每创建一个对象,函数 sayHi 都会被重新创建一次,这个时候,每个对象都拥有一个独立的,但是功能完全相同的方法,这样势必会造成***内存浪费***。有的人可能会想,既然是一样的那我们就单独把它提出来,写一个函数,每次调用不就可以了吗?但是这样做会导致全局变量增多,可能会引起命名冲突,代码结果混乱,维护困难。通过使用***原型(prototype)***可以很好的解决这个问题。

在 JavaScript 中,每一个函数都有一个 prototype 属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承。我们来看看前面例子原型的写法:

function Student(name, age, gender) {
    this.name = name;
    this.age = age;
    this.gender = gender;
}
Student.prototype.sayHi = function(){
    console.log("hi");
}
var s1 = new Student('zhangsan', 18, 'male');
s1.sayHi();//打印 hi
var s2 = new Student('lisi', 18, 'male');
s2.sayHi();//打印 hi
console.log(s1.sayHi == s2.sayHi); // 结果为true

构造函数的prototype对象默认都有一个constructor属性,指向prototype对象所在函数

function F() {}
console.log(F.prototype.constructor === F);//结果为ture

通过构造函数得到的实例对象内部会包含一个指向构造函数的 prototype 对象的指针__proto__。__proto__属性最早是火狐浏览器引入的,用以通过实例对象来访问原型,这个属性在早期是非标准的属性。

function F() {}
var a = new F();
console.log(a.__proto__ === F.prototype); //结果为true

实例对象可以直接访问原型对象成员。所有实例直接或间接继承了原型对象的成员。
总结:每一个构造函数都有一个原型对象prototype,原型对象包含一个constructor属性指向 原型对象的所在函数,而实例都包含一个内部指针__proto__指向原型对象。

【原型链】:所有的对象都有原型,而原型也是对象,也就是所原型也有原型,那么如此下去,也就组成了我们所说的原型链。
属性搜索原则:属性搜索原则,也就是属性的查找顺序,在访问对象的成员的时候,会遵循以下原则:
(1)首先从对象实例本身开始找,如果找到了这个属性或者方法,则返回。
(2)如果对象实例本身没有找到,就从它的原型中去找,如果找到了,则返回。
(3)如果对象实例的原型中也没找到,则从它的原型的原型中去找,如果找到了,则返回。
(4)一直按着原型链查找下去,找到就返回,如果在原型链的末端还没有找到的话,那么如果查找的是属性则返回 undefined,如果查找的是方法则返回 xxx is not a function。

在前面的例子中,我们是使用 xxx.prototype. 然后加上属性名或者方法名来写原型,但是每添加一个属性或者方法就写一次显得有点麻烦,因此我们可以用一个包含所有属性和方法的对象字面量来重写整个原型对象:

function Student(name, age, gender) {
    this.name = name;
    this.age = age;
    this.gender = gender;
}
Student.prototype = {
    hobby:"study",
    sayHi:function(){
    console.log("hi");
    }
}
var s1 = new Student("wangwu",18,"male");
console.log(Student.prototype.constructor === Student);//结果为 false

但是这样写也有一个问题,那就是原型对象丢失了 constructor 成员。所以为了保持 constructor 成员的指向正确,建议的写法是:

function Student(name, age, gender) {
    this.name = name;
    this.age = age;
    this.gender = gender;
}
Student.prototype = {
    constructor: Student, //手动将 constructor 指向正确的构造函数
    hobby:"study",
    sayHi:function(){
    console.log("hi");
    }
}
var s1 = new Student("wangwu",18,"male");
console.log(Student.prototype.constructor === Student);//结果为 true

【原型链继承】:主要思想是利用原型让一个引用类型继承另外一个引用类型的属性和方法。

function Student(name, age, gender) {
    this.name = name;
    this.age = age;
    this.gender = gender;
}
Student.prototype.sayHi = function(){
    console.log("hi");
}
var s1 = new Student("zhangsan",18,"male");
s1.sayHi(); //打印 hi
var s2 = new Student("lisi",18,"male");
s1.sayHi(); //打印 hi

上述例子中实例化对象s1和s2都继承了sayhi方法。

面向对象编程是 JavaScript 编程的一种主流方式,这种方式可以提高代码的易读性,减少代码的冗余,降低代码设计的难度。

猜你喜欢

转载自blog.csdn.net/Zhou07182423/article/details/89335299
0条评论
添加一条新回复