一、函数 函数原型 函数实例
函数是function关键字声明的函数,函数原型有个原型链的概念,每个构造函数都是保存在原型中prototype,prototype是函数的原型,原型中有个constructor,函数前面加new 关键字,就是函数的实例化,生成的就是函数实例。
1.函数
函数是function关键字声明的函数,也就是fun函数本身,也叫做构造函数。在创建函数fun的时候,也会自动为它创建一个prototype属性,这个属性的作用就是用来指向函数原型(原型对象)。prototype可以理解为fun函数的一个属性,保存着原型函数的引用。
function fun(){
}
2.函数实例
函数前面加new就是函数的实例化,生成的就是函数实例。f就是通过new fun()得到的函数实例。f的内部会有一个包含函数原型的指针[[prototype]],这时候f可以调用函数原型的属性和方法。但是[[prototype]]是内部属性,无法直接访问。
var f = new fun();
那要什么解决这个问题呢?当然有方法可以访问内部属性。以下提供了两种方法
//_proto_:部分浏览器提供了此属性去访问[[prototype]]属性的值
//通过Object.getPrototypeOf去获取
3函数原型对象
函数原型对象有一个constructor的属性,这个属性指向包含一个指向prototype属性的所在函数的指针(f函数)
attention:
-
每一个构造函数都有一个原型对象prototype,原型对象上包含着构造函数的指针constructor,而函数实例都包含着一个指向原型对象的的内部指针_ proto _。实例可以通过内部指针访问到原型对象,原型对象可以通过constructor找到构造函数。
-
对象没有prototype属性,只有方法才有prototype。
-
任何对象都有一个原型对象,任何对象都有一个constructor属性,指向创建此对象的构造函数,{}对象,它的构造函数是function Object(){};
二、class继承
- ES6之前
function Student(name){
this.name = name;
}
//给student新增一个方法
Student.prototype.hello = function(){
alter('hello');
}
- ES6引入class关键字,直接定义一个类(属性、方法)
class Student{
constructor(name){
this.name = name;
}
hello(){
alter('hello');
}
}
三、原型链
引用对象属性:首先在对象(instance)的内部寻找该属性,找不到再去该对象的原型(instance.prototype)里去找这个属性
如果让原型对象指向另一个类型的实例 constructor1.prototype = instance2
试图引用constructor1构造的实例instance1的某个属性p1:
(1)在instance内部属性寻找一次
(2)在instance1.proto(construct1.prototype)中找一遍,但是constructor1.prototype = instance2,也就是说在instance2中找属性p1
(3)如果在instance2中没有找到,它会继续在instance2.proto(constructor2.prototype)中寻找…直至Object的原型对象
因为搜索的轨迹像一条长链:搜索轨迹: instance1–> instance2 –> constructor2.prototype…–>Object.prototype
于是把这种实例与原型的链条叫做原型链。
//instance实例通过原型链找到了Father原型中的getFatherValue方法
function Father(){
this.property = true;
}
father.prototype.getFatherValue = function(){
return this.property;
}
function Son(){
this.sonProperty = false;
}
//继承Father
Son.prototype = new Father();//Son.prototype被重写,导致Son.prototype.constructor也一同被重写
Son.prototype.getSonValue = function(){
return this.sonProperty;
}
var instance = new Son();//instance.constructor指向的是Father,因为Son.prototype中的constructor被重写。
alert(instance.getFatherValue());//true
-
对象分为普通对象和函数对象,Function、Object是js自带的对象。通过new Function()创建的都是函数对象,其他都是普通对象。普通对象没有prototype,但有__proto__属性。
-
确定原型和实例的关系
(1)通过操作符 instanceof
测试结果表明,instance是Object Father Son 中任何一个类型的实例,所以三个构造函数都返回了true
(2)isPrototypeOf()方法,只要原型链中出现过的原型,isPrototypeOf()就会返回true
- 原型链的问题
(1)当原型链中包含引用类型值的原型时,该引用类型值会被所有实例所共享。
(2)在创建子类型(Son)的时候,不能向超类型(Father)的构造函数传递参数
怎么解决问题??继承!!!
四、继承
1.借用构造函数(constructor stealing)也叫做经典继承
基本思想:在子类构造函数中调用父类构造函数,可以在子类构造函数中使用call()
和apply()
方法
<script>
function Father(){
this.colors = ["red","blue","green"];
}
function Son(){
Father.call(this);//继承Father,且向父类传递参数
}
//引用类型值独立
var instance1 = new Son();
console.log(instance1.colors);
var instance2 = new Son();
console.log(instance2.colors);
</script>
优点:
(1)可以在子类构造函数中向父类传递参数
(2)父类的引用属性不会被所有实例共享,保证原型链中引用类型值的独立
缺点:
(1)方法都在构造函数中定义,函数复用不可用,超类型(Father)类中定义的方法对子类型而言也是不可见的。因此很少用
2.原型链继承
基本思路:将父类的实例作为子类的原型
<script>
function Father(){
this.value = true;
this.table = {
name:"gaby",
age: 21,
};
}
Father.prototype.getTable = function(){
console.log(this.table);
console.log(this.value);
}
function Son(){};
Son.prototype = new Father();//父类的实例作为子类的原型
let Son1 = new Son();
Son1.table.gender = "man";
Son1.getTable();
let Son2 = new Son();
Son2.getTable();
Son2.value = false;
console.log(Son2.value);
</script>
优点:
(1)父类的方法可以复用
缺点:
(1)父类的属性会被所有的子类所共享,更改一个子类的引用属性,其他子类也会受影响。
(2)子类型实例不能给父类构造函数传参
3.组合继承(伪经典继承)
将原型链和构造函数的技术组合到一起
基本思路:使用原型链实现对原型方法和属性的继承,用构造函数实现对实例的继承
<script>
function Father(name){
this.name = name;
this.colors = ["red","blue","green"];
}
Father.prototype.getName = function(){
alert(this.name);
}
function Son(name,age){
Father.call(this,name);//继承实例属性,1调用Father()
this.age = age;
}
Son.prototype = new Father();//2调用Father()
Son.prototype.getAge = function(){
alert(this.age);
}
var instance1 = new Son("gaby",20);
console.log(instance1.colors);
instance1.getName();
instance1.getAge();
var instance1 = new Son("prada",21);
console.log(instance1.colors);
instance1.getName;
instance1.getAge;
</script>
优点:
(1)js常用的继承模式,instanceof()和isPrototypeOf()也能用于识别基于组合继承创建的对象。
(2)父类的方法可以复用
(3)可以在instance构造函数中向Father构造函数传递参数
(4)父类构造函数中的引用属性不会被共享
缺点:
(1)调用两次父类,造成不必要的内耗
4.原型式继承
借助原型基于已有的对象创建新的对象,同时还不必因此创建自定义类型。对参数对象的一种浅复制
基本思路:在object()函数内部,先构建一个临时性的函数,然后将传入的对象作为这个构造函数的原型,最后返回这个临时类型的一个新实例。
<script>
function objectCopy(obj){
function f(){};
f.prototype = obj;
return new f();
}
let person = {
name:"gaby",
age:20,
friends:["mike","amy","linda"],
setName:function(){
console.log(this.name);
}
}
let person1 = objectCopy(person);
person1.name = "xiaoming";
person1.friends.push("adam");
person1.setName();
let person2 = objectCopy(person);
person2.name = "xiaohong";
person2.friends.push("lily");
person2.setName();
console.log(person.friends);
</script>
优点:
(1)父类的方法可以复用
缺点:
(1)父类的引用会被子类所共享
(2)子类不能向父类传参
ES5新增object.create() 方法规范化了上面的原型式继承.
object.create() 接受两个参数,一是用作新对象原型的对象,二是(可选的)一个为新对象定义额外属性的对象
5.寄生式继承
基本思路:创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再返回对象。使用原型式继承对一个目标对象进行浅复制,增强这个浅复制的能力。
<script>
function objectCopy(obj){
function f(){};
f.prototype = obj;
return new f();
}
function createPerson(original){
let clone = objectCopy(original);//通过调用objectCopy函数创建一个新对象
clone.getName = function(){
console.log(this.name);//以某种方式增强新对象
}
return clone;
}
let person = {
name:"gaby",
friends:["lily","tom","mike"]
}
let person1 = createPerson(person);
person1.friends.push("brone");
console.log(person1.friends);
person1.getName();
let person2 = createPerson(person);
console.log(person2.friends);
</script>
缺点:函数不能复用
6.寄生式组合继承
组合继承缺点:无论什么情况都会调用两次父类构造函数,一次是在创建子类原型的时候,另一次是在子类构造函数内部
寄生式组合继承出现就是为了解决这个问题,减低调用父类构造函数。
基本思路:不必为了指定子类型函数的原型而去调用超类型的构造函数
<script>
function objectCopy(obj){
function f(){};
f.prototype = obj;
return new f();
}
function inheritPrototype(Son,Father){
let prototype = objectCopy(Father.prototype);//创建对象
prototype.constructor = Son;//增强对象
Son.prototype = prototype;//赋值对象
}
function Father(name){
this.name = name;
this.friends = ["lily","tom","mike"]
}
Father.prototype.sayName = function(){
console.log(this,name);
}
inheritPrototype(Son,Father);
Son.prototype.sayAge = function(){
console.log(this.age);
}
let Son1 = new Son("gaby",20);
Son1.sayName();
Son1.sayAge();
Son1.friends.push("brone");
console.log(Son1.friends);
let Son2 = new Son("gaby",20);
Son2.sayName();
Son2.sayAge();
Son2.friends.push("brone");
console.log(Son2.friends);
</script>
五、碎碎念
这一部分的内容是前端JavaScript面试高频考点,要好好理解一下,向下扎根。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/16168.html