js原型的那些事
面向对象
将所有的物体抽象为一个类,每一个具体的个体是类的实例化对象。构造函数是创建类的方法,可以无限制创建类,相当于工厂。
数据类型中的类是一种数据类型,而面向对象是一种编程思想,更高级的还有函数式编程。面向对象里面也有属性和方法。有构造函数创建。为了避免资源的浪费,再构造函数里面创建实例化对象的属性,再构造函数的原型上创建对象的方法。
- 模板: ——> 构造函数是用来生成对象的,
- php python 是一种面向对象的编程语言 -> 以类为创建对象的模板
- JavaScript 是基于对象的编程语言 -> 以构造函数为模板 ->动态语言:没有定义数据类型,需要时才会去处理
1.构造函数
创建构造函数
- 不能使用箭头函数
- 首字符大写的函数
- 建议不使用函数表达式,如果以函数表达式创建箭头函数,后面进行继承操作的时候,由于需要给子的构造函数还原,若没有名字,则还原之后他的constructor 不具名。
1 | const Animal = function(){} |
在构造函数中,一般只写实例化对象的属性,将实例化对象的方法写在构造函数的原型上,形如
function Animal(name,sex){ this.name = name; this.sex = sex; } Animal.prototype.play = function(){ console.log('play'); }
在构造函数中使用this 关键字,this 指向未来即将生成的实例化对象1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
new 关键字
- 原理:
- 创建一个空对象
- 空的原型
- this 的指向变为空对象
- ```js
const cat = new Cat('实际参数')
//若不需要传递参数,则后面的()可以省略
//形如
const cat = new Car1
2
3
4//为了保证实例化对象的时候,使用new 关键字,可以在构造函数内部使用,严格模式
//如果在实例化对象的时候,没有使用new 关键字系统就会报错
....
若实例化时没有加new,实例化对象为未定义,而对象的属性直接报错1
2
3
4
5
6
7
8function Car(){
this.name = 'nem';
this.sex = 'cute';
}
const car =Car();
console.log(car);
// underfinded构造函数返回值
- 构造函数的返回值如果时基础数据类型,对实例化对象没有影响。
- 如果构造函数的返回值是引用数据类型,则会覆盖掉构造函数内部的属性。
- 故构造函数不能有返回值
new.target命令
函数内部可以使用new.target
属性。如果当前函数是new
命令调用,new.target
指向当前函数,否则为undefined
。
1 | function f() { |
使用这个属性,可以判断函数调用的时候,是否使用new
命令。
1 | function f() { |
上面代码中,构造函数f
调用时,没有使用new
命令,就抛出一个错误。
Object.create( ) 创建实例对象
通过 Object.create 创建的对象原型为空。
1 | //用于继承 |
如果需要一个构造函数的内容的部分包含另一个构造函数的内容,并且又有自己的
属性,该怎么写
1
2
3
4
5
6
7
8
9
10
11
12function Animal(name,sex){
this.name = name;
this.sex = sex;
}
function Cat(name,sex,scorlls){
Animal.call(this,name,sex);
this.scorlls = scorlls;
}
//此处的call也可替换为apply,但后面参数要写成数组的形式
Animal.apply( this,[name,sex])
2.原型和原型链
一、prototype
在JavaScript中,每个函数都有一个prototype属性,这个属性指向函数的原型对象。并且这个属性是一个对象数据类型的值.
除了underfinded 和 null 以外,所有的数据类型的原型都是Oject
Person ------ prototype --- Perosn.prototype
(构造函数) ---> (实现原理)
二、proto
这是每个对象(除null外)都会有的属性,叫做__proto__,这个属性会指向该对象的原型。
三、constructor
每个原型都有一个constructor属性,指向该关联的构造函数。
1 | function Person() { |
四、实例与原型
当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性,如果还查不到,就去找原型的原型,一直找到最顶层为止。
五、原型的原型
在前面,我们已经讲了原型也是一个对象,既然是对象,我们就可以用最原始的方式创建它,那就是:
1 | var obj = new Object(); |
其实原型对象就是通过 Object 构造函数生成的,结合之前所讲,实例的 proto 指向构造函数的 prototype ,所以我们再更新下关系图:
1 | //原型的最上面是object |
六、原型链
简单的回顾一下构造函数、原型和实例的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。那么假如我们让原型对象等于另一个类型的实例,结果会怎样?显然,此时的原型对象将包含一个指向另一个原型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立。如此层层递进,就构成了实例与原型的链条。这就是所谓的原型链的基本概念。——摘自《javascript高级程序设计》
1 | //而原型链中就是实例对象和原型对象之间的链接。每个函数都有一个prototype属性,这个prototype属性就是我们的原型对象,我们拿这个函数通过new构造函数创建出来的实例对象,这个实例对象自己会有一个指针(_proto_)指向他的构造函数的原型对象!这样构造函数和实例对象之间就通过( _proto_ )连接在一起形成了一条链子。 |
在JavaScript中 实例化对象与原型之间的链接,叫做原型链,其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法,然后层层递进,就构成了实例与原型的链条,这就是所谓的原型链
1).为什么要使用原型链呢?
1.为了实现继承,简化代码,实现代码重用!
2.只要是这个链条上的内容,都可以被访问和使用到!
2).使用原型链有什么作用?
继承
prototype用来实现基于原型的继承与属性的共享
避免了代码冗余,公用的属性和方法,可以放到原型对象中,这样,通过该构造函数实例化的所有对象都可以使用该对象的构造函数中的属性和方法!
减少了内存占用
3).原型链的特点
就近原则,当我们要使用一个值时,程序会优先查找离自己最近的,也就是本身有没有,如果自己没有,他就会沿着原型链向上查找,如果还没有找到,它还会沿着原型链继续向上查找,找到到达Object
引用类型,当我们使用或者修改原型链上的值时,其实使用的是同一个值!
JS中每个函数都存在原型对象属性prototype。并且所有函数的默认原型都是Object的实例。
每个继承父函数的实例对象都包含一个内部属性_proto_。该属性包含一个指针,指向父函数的prototype。若父函数的原型对象的_proto_属性为再上一层函数。在此过程中就形成了原型链。
4).ES5继承
1 | //Es5 继承时,函数尽量不使用函数声明语法 |
5).ES6继承(语法糖)
1 | class Animal{ |
能被继承
extends的主要用于子类继承父类,继承之后子类拥有父类的的所有方法包括,静态方法和属性
1 | class A { |
6).原型链覆盖
1 | fn.prototype = { ...} |
3.proto,prototype 和constructor之间的关系
1 | class Animal{ |
4.call bind apply
1 | //第一个参数 都是this 的指向,后面的参数则都是传递的参数 |
1 | // Array.prototype ---》数组的实例化对象的原型 ---》this 指向数组的实例化对象 |
1 | const btn = document.querySelector('#btn'); |
1 | function Person(name,age){ |
1 | function Person(name,age){ |