目录
利用工厂函数实例化对象
利用构造函数实例化对象
什么是原型和原型链
原型的继承
继承属性
继承方法
工厂函数:顾名思义,就类似一个工厂,批量化生产某种类型的东西
在工厂函数内部通过new关键字调用构造函数来批量实例化对象,通过对象.属性的方式赋值,最终返回对象
function demo(uname,age){
let obj = new Object();
obj.uname = uname;
obj.age = age;
return obj;
}
let obj = demo("Ammy",19);
console.log(obj);
构造函数:在 JavaScript 中,用 new 关键字来调用的函数,称为构造函数。构造函数首字母一般大写,采用this关键字赋值
function Person(uname,age,sex){
this.uname = uname;
this.age = age;
this.sex = sex;
}
let p = new Person("Sara",19,"女");
let p1 = new Person("Tom",18,"男");
console.log(p,p1);
function Person(uname,age,sex){
this.uname = uname;
this.age = age;
this.sex = sex;
this.fn = function(){
console.log(this.uname+"今年"+this.age+"岁了-------"+this.sex);
}
}
let p1 = new Person("Tom",19,"男");
let p2 = new Person("Ammy",18,"女");
console.log(p1.fn === p2.fn);
实例化多个对象的同时,若想要为这个对象添加一些共有的方法,直接在这个构造函数内部写方法fn(),这个fn()方法也会被执行多次,但是方法都是同一个,这也产生了不必要的资源浪费,于是我们引出了原型和原型链的概念,在原型对象上去存放公共的属性及方法
原型:js中的声明构造函数时会自动在实例化对象内部创建一个对象(__proto__),即原型(对象),存放公共属性及方法
原型链:当访问实例化对象里面的方法或属性时,第一时间会在对象本身内部去找,若没有,则会上它的原型(对象.__proto__)上去找,原型上也没有,则去往原型的原型上去找,即实例对象._proto__.__proto__或者构造方法.prototype.__proto__上找,这个层层访问的过程形成了一个原型链
原型对象的作用:存放公共属性即方法
构造函数访问原型对象的方式:构造函数.prototype
实例化对象访问原型对象的方式:实例化对象.__proto__
构造函数一旦创建,原型及原型链就已经形成
构造函数通过new关键字创建一个实例化对象,再通过.__proto__访问原型对象,原型对象通过.constructor访问一开始的构造函数形成了一个圈,原型对象也是一个对象,它又有它的原型对象,同样通过.__proto__可访问原型对象的原型对象,产生此原型对象的构造函数为Object()
,这个原型对象的原型对象没有产生它的构造函数了,故此原型对象的原型对象最终指向了null
function Person(uname,age,sex){
this.uname = uname;
this.age = age;
this.sex = sex;
}
let p =new Person("Sara",18,"女");
console.log(p.__proto__);
console.log("原型对象----prototype",Person.prototype);
console.log(p.__proto__ === Person.prototype);
function Person(uname,age,sex){
this.uname = uname;
this.age = age;
this.sex = sex;
}
let p1 = new Person("Tom",19,"男");
let p2 = new Person("Ammy",18,"女");
Person.prototype.fn = function(){
console.log("这是原型上的方法");
}
console.log(p1.fn===p2.fn);
将公共的方法写在构造函数的原型对象上就可以工这个构造函数的实例化对象共享使用,减少资源浪费,下面讲一讲关于原型的继承
继承分为属性继承和方法继承
父类构造函数
function Father(name,age){
// this指向创建它的实例化对象
this.name = name;
this.age = age;
}
现在要写一个Son子类构造函数,它也同样拥有name和age属性,不需要将上面的代码再写一遍,可以这样写
因为直接调用Father()构造方法,它指向的是创建它的实例化对象,需要通过call()方法来改变this的指向,让this指向当前构造函数的实例对象,
子类新有的属性通过this关键字直接添加即可
子类构造函数
function Son(name,age,email){
// this指向Son的实例化对象
// 使用call()改变this指向,立即调用父类方法并传递参数【继承父类属性】
Father.call(this,name,age);//this让原本指向Father()实例化对象 指向了Son()的实例化对象
// 子类 自身属性
this.email = email;
}
let f = new Father("Tom",50);
let s = new Son("Eriv",23,"[email protected]");
console.log(f,s);
例Father的原型对象f1,Son的原型对象s1,,让Son继承Father的方法
因为方法共有方法都是写在原型对象上的,例如当Father()的实例对象可以赚钱,Son()的实例对象本没有这个方法,要让Son()的实例对象也能赚钱,鉴于这个赚钱方法写在Father的原型对象上,s1自身没有这个方法,回去它的原型对象上找,首先我们会想到让s1的原型对象直接指向f1的原型对象,可以达到我们当前的需求,但是在我们给s1添加新的方法时,f1也可以访问,这就实现了方法互通,没有了私有的部分,所有,我们可以让Father再实例化一个对象f2,让这个f2的作为一个桥梁实现继承,让s1的原型对象指向f2,那么s1.__proto__就指向了f2,s1.__proto__.__proto__就指向了父类构造的原型对象,也就实现了方法的继承,由于这个实力对象对于子类构造来说是一个原型对象,原型对象要指回子类的构造方法。
故综上所述,继承父类的方法有三步:
1、先再实例化一个父类构造的实例对象 let f2 = new Father();
2、让(子类的实例对象/子类构造的原型对象)指向第一步的实例对象 Son.prototype = f2 / s1.__proto__=f2;
3、再让这个第一步中的实例对象指向子类的构造函数 f2.constructor = Son;
原始方法的代码如下:(会实现方法互通,没有了自己的构造函数特有的方法,不可行)
function Father(name,age){
// this指向创建它的实例化对象
this.name = name;
this.age = age;
}
// 子类 构造函数
function Son(name,age,email){
// this指向Son的实例化对象
// 使用call()改变this指向,立即调用父类方法并传递参数【继承父类属性】
Father.call(this,name,age);//this让原本指向Father()实例化对象 指向了Son()的实例化对象
// 子类 自身属性
this.email = email;
}
Father.prototype.money = function(){
// this----->调用这个函数的实例化对象
console.log(this.name+"在赚钱");
}
Son.prototype = Father.prototype;
let f1 = new Father("Tom",50);
Son.prototype.game = function(){
console.log(this.name+"在玩游戏");
}
let s = new Son("Eriv",23,"[email protected]");
f1.money();
s.money();
s.game();
f1.game();
正确方式:需将注释的这一行代码修改如下:能够实现方法的继承,也能有保留自己特有的方法
//Son.prototype = Father.prototype;
let f1 = new Father("Tom",50);
let f2 = new Father();
Son.prototype = f2;
f2.constructor = Son;