在ES6之前,对象并不是基于类创建的,而是构造函数来定义对象及其特征。
构造函数是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总与 new 一起使用。我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面。
而new在执行时会有四件事情:
静态成员及实例成员
下面这个例子可以帮助你理解:
function Stra(uname, age) {
//构造函数里面的属性和方法我们叫做成员
this.uname = uname;
this.age = age;
this.sing = function() {
console.log("sing~~~");
}
}
var ldh = new Stra('ldh', 18);
//实例成员就是构造函数内部通过this添加的成员 sing uname age...
//实例成员只能通过实例化对象访问
console.log(ldh.age);
// console.log(Stra.age); //undefined
//静态成员 在构造函数本身添加成员
Stra.sex = 'male'; //这里就是静态成员
//只能通过构造函数访问
console.log(Stra.sex);
// console.log(ldh.sex); //undefined
我们先看一下这个例子:
function Star(uname, age) {
//构造函数里面的属性和方法我们叫做成员
this.uname = uname;
this.age = age;
}
Star.prototype.sing = function() {
console.log("sing~~~");
}
var ldh = new Star('ldh', 18);
var zxy = new Star('zxy', 19);
// 一般公共属性定义到构造函数,方法定义到原型对象
console.log(ldh.sing === zxy.sing); //false
我们构造两个对象,判断一下两个对象的sing方法是否在同一内存,答案是false。这样就造成了内存资源的浪费。我们希望所有的对象使用同一个函数,这样就比较节省内存,那么我们要怎样做呢?
这时候我们就要提出一个构造函数的属性—prototype(构造函数原型)。
构造函数通过原型分配的函数是所有对象所共享的。
JavaScript 规定,每一个构造函数都有一个 prototype 属性,指向另一个对象。注意这个 prototype 就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有。
我们可以把那些不变的方法,直接定义在 prototype 对象上,这样所有对象的实例就可以共享这些方法,就可以解决掉内存资源浪费的问题。
function Star(uname, age) {
//构造函数里面的属性和方法我们叫做成员
this.uname = uname;
this.age = age;
}
Star.prototype.sing = function() {
console.log("sing~~~");
}
var ldh = new Star('ldh', 18);
var zxy = new Star('zxy', 19);
console.log(ldh.sing === zxy.sing); //true
这时候再判断,两个对象的sing方法指向就相同了。
下面我们再介绍一个概念:对象原型 __ proto__
每一个对象都会有一个__proto__属性,指向构造函数的prototype原型对象。
是不是有些晕,看下面这张图,它能帮助你更直观的理解:
constructor构造函数
对象原型( proto)和构造函数(prototype)原型对象里面都有一个属性 constructor 属性 ,constructor 我们称为构造函数,因为它指回构造函数本身。
constructor 主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数。
一般情况下,对象的方法都在构造函数的原型对象中设置。
我们通过输出一个构造函数的prototype属性,可以看到里面存在一个constructor属性,它是一个函数,指向该构造函数。
构造函数、实例、原型对象三者的关系
原型链
我们知道只要是一个对象就有__proto__属性,同时我们还知道一个构造函数的prototype指向一个对象,那么它的__proto__属性是什么呢,我们试着打印一下。
function Star(uname, age) {
this.name = uname;
this.age = age;
}
Star.prototype.sing = function() {
console.log("sing~~~");
}
var ldh = new Star('ldh', 18);
// 1、只要是对象就有__proto__属性,指向原型对象
console.log(Star.prototype.__proto__);
// Object.prototype里面的__proto__指向为null
它指向一个对象Object(),它会像套娃一样无止尽的指下去吗?答案是不会的,Object.prototype里面的__proto__指向为null。
这里有一张图,更加直观:
了解完了原型对象,接下来我们讲一个实用性的操作,利用原型对象扩展内置对象:
我们知道Array()对象里面有很多使用的方法,比如索引了、排序了之类的,但是我们相对数组求和的时候却发现没有这个方法,那么我们用原型对象来给Array()加一个sum求和方法吧。
我们先打印一下Array的原型对象:
里面有很多方法,但是没有我们想要的sum求和方法。
我们加一个试试:
// 添加array对象的求和功能
Array.prototype.sum = function() {
var sum = 0
for (var i = 0; i < this.length; i++) {
sum += this[i];
}
return sum;
}
// var arr = [1, 2, 3, 4];
console.log(Array.prototype);
可以看到Array()里面已经有了sum方法,能不能用呢?我们实践一下:
// 添加array对象的求和功能
Array.prototype.sum = function() {
var sum = 0
for (var i = 0; i < this.length; i++) {
sum += this[i];
}
return sum;
}
// var arr = [1, 2, 3, 4];
var arr = new Array(1, 2, 3);
console.log(arr.sum()); // 6
console.log(Array.prototype);
打印结果是6,看来可以准确地输出结果呢。
这里有一点需要注意
内置对象不能给原型对象覆盖操作 Array.prototype = {} ,只能是 Array.prototype.xxx = function(){} 的方式。
ES6之前并没有给我们提供 extends 继承。我们可以通过构造函数+原型对象模拟实现继承,被称为组合继承。
es6之前是通过构造函数+原型对象实现继承的
call()可以调用这个函数并且修改函数运行时this的指向
方法:fun.call(thisArg, … ,);
实例:
function fun(x, y) {
console.log("drink water");
console.log(this);
console.log(x + y);
}
var o = {
name: "andy"
}
// 调用函数
// fun.call();
// 修改this的指向
fun.call(o, 1, 2); // 此时this指向o
这里我们在调用的时候,发现fun()中this的指向已经改变。
原理: 通过 call() 把父类型的 this 指向子类型的 this ,这样就可以实现子类型继承父类型的属性。
// 父构造函数
function Father(uname, age) {
this.name = uname;
this.age = age;
}
// 子构造函数
function Son(uname, age, score) {
Father.call(this, uname, age);
this.score = score;
}
var son = new Son('ldh', 18, 100);
console.log(son);
在子构造函数中,通过调用Father.call()方法可以更改Father的this指向,让其指向Son,这样就可以实现继承Father的属性了。那父亲的方法呢?我们一般使用原型对象。
一般情况下,对象的方法都在构造函数的原型对象中设置,通过构造函数无法继承父类方法。
核心原理:
举个例子
父亲有挣钱的方法,儿子想从父亲那里继承该方法,并且还要有一个考试的方法。
先解决问题一:继承父亲的方法
我们可以使用Son的原型对象,指向父亲的实例化对象,这样Son的原型对象里面就有了earn()方法。
可以看到现在Son的prototype里面的prototype有了earn方法。
接下来是问题二:添加儿子的方法
直接在Son的原型对象里面添加exam方法
最后注意将Son原型对象的constructor函数指回Son。
// 父构造函数
function Father(uname, age) {
this.name = uname;
this.age = age;
}
Father.prototype.earn = function() {
console.log(10000);
}
// 子构造函数
function Son(uname, age, score) {
Father.call(this, uname, age);
this.score = score;
}
Son.prototype = new Father();
Son.prototype.constructor = Son;
Son.prototype.exam = function() {
console.log("考试");
};
var son = new Son('ldh', 18, 100);
console.log(son);
放个关系图,助于理解:
下一篇:JavaScript高级— (3)ES5新方法,数组、字符串、对象
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/qq_46186155/article/details/120226540
内容来源于网络,如有侵权,请联系作者删除!