文章可能有点长,但 🌰 栗子🌰 都很经典,简单易懂,一起学习吧!
从决定做前端开发就开始学习JS,一直到现在,虽然学了很多,听了很多课,看了很多大佬的博客,但是在工作中都不怎么用,印象还是不太深刻,现在就重新学一下,也做一下笔记,把自己见解与学到的东西和大家分享一下,同时也方便自己以后复习。
壹 ❀ 引 ---- 构造函数
说到原型与原型链,那就不得不提一下构造函数!
** 构造函数就是一个普通的函数,创建方式和普通函数没有区别;不同的是构造函数习惯上首字母大写。另外就是调用方式的不同,普通函数是直接调用,而构造函数需要使用new关键字来调用**。
function Person(name, age, gender) {
this.name = name
this.age = age
this.gender = gender
this.sayName = function () {
alert(this.name);
}
}
var per = new Person("孙悟空", 18, "男");
function Dog(name, age, gender) {
this.name = name
this.age = age
this.gender = gender
}
var dog = new Dog("旺财", 4, "雄")
console.log(per);//当我们直接在页面中打印一个对象时,事件上是输出的对象的toString()方法的返回值
console.log(dog);
每创建一个Person构造函数,在Person构造函数中,为每一个对象都添加了一个sayName方法,也就是说构造函数每执行一次就会创建一个新的sayName方法。这样就导致了构造函数执行一次就会创建一个新的方法,执行10000次就会创建10000个新的方法,而10000个方法都是一摸一样的,为什么不把这个方法单独放到一个地方,并让所有的实例都可以访问到呢?这就需要原型(prototype)
贰 ❀ 原型
在JS中,每当定义一个函数数据类型
(普通函数、类)
时候,都会天生自带一个prototype属性,这个属性指向函数的原型对象,并且这个属性是一个对象数据类型的值。
![](https://img-blog.csdnimg.cn/29f10e360d8e435aa165f83186ec612b.png)
原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将对象中共有的内容,统一设置到原型对象中。
function Person(name,age){
this.name=name
this.age=age
this.sayName = function () {
alert(this.name);
}
}
改写为:
function Person(name,age){
this.name=name
this.age=age
}
Person.prototype.year=2021 // 往原型上添加一点东西
Person.prototype.sayName =function(){
alert(this.name);
}
const personA=new Person()
const personB=new Person()
console.log('personA',personA , personB)
console.log(personA.hasOwnProperty('year'), 'year' in personA ) // false true
console.log(personB.hasOwnProperty('year'), 'year' in personB) // false true
// hasOwnProperty只会从实例本身上找属性, in会从实例所属类的原型身上找
console.log('personA.year',personA.year , personB.year ) // 2021 2021
原型的作用:
1:实现对象之间的数据共享。
2.在es6之前,没有class的情况下,模拟面向对象,构造函数中放私有属性,原型上放公有属性,一般放方法。通过原型添加的方法,可以完美的解决属性与方法共享问题,从而节省了内存空间
❀加餐❀ ---- 加深对原型的理解
5个原型规则
1.所有引用类型(数组、对象、函数),都具有对象特性,及可自由扩展属性;
举个栗子 var obj = { name:'波妞' } console.log(obj) // { name: '波妞' } obj.like = '宗介' console.log(obj) // { name: '波妞', like: '宗介' }
2. **所有的引用类型都有一个__proto__属性,属性值是一个普通对象;** 3. **所有的函数都有一个prototype属性,属性值是一个普通对象;** 4. **所有的****引用类型的__proto__指向它构造函数的prototype属性值;** 5. **当试图获取一个对象的某个属性时,如果这个对象本身没有这个属性,那么他会去它的__proto__(即它构造函数的prototype)去找;**
<body>
<!--
###### 原型 ######
1. 函数的prototype属性(图)
* 每个函数都有一个prototype属性, 它默认指向一个Object空对象(即称为: 原型对象)
* 原型对象中有一个属性constructor, 它指向函数对象
2. 给原型对象添加属性(一般都是方法)
* 作用: 函数的所有实例对象自动拥有原型对象中的属性(方法)
或者理解为:原型对象的属性与其对应的实例对象是可见的(实例对象可以访问)
-->
<script>
// 1. 每一个函数都有一个prototype属性,它默认指向一个object空对象(即称为:原型对象)
console.log(Date.prototype, typeof(Date.prototype));
function Fun(){
}
console.log(Fun.prototype, typeof(Fun.prototype));
// 2. 原型对象中有一个属性constructor, 它指向函数对象
console.log(Date.prototype.constructor === Date); // true
console.log(Fun.prototype.constructor === Fun); // true
// 3. 给原型对象添加属性(一般都是方法) ===> 实例对象可以访问
// 此时 fun.prototype是一个空对象(原型对象), js有一个特点,可以为对象动态添加属性
Fun.prototype.test = function(){
console.log("test()");
}
// 4. 作用: 函数的所有实例对象自动拥有原型中的属性(方法)
var fun = new Fun(); // 创建一个实例对象
fun.test();
</script>
</body>
<!--
###### 显示原型与隐式原型 ######
1. 每个函数function都有一个prototype属性,即显式原型(属性) 默认指向一个空object对象
2. 每个实例对象都有一个__proto__属性,即隐式原型(属性) -- 所有的引用类型(包括数组,对象,函数)都有隐性原型属性(__proto__),值也是一个普通的对象。
3. (实例)对象的隐式原型的值为其对应构造函数的显式原型的值
4. 内存结构(图)
5. 总结:
* 函数的prototype属性: 在定义函数时自动添加的, 默认值是一个空Object对象
* 对象的__proto__属性: 创建对象时自动添加的, 默认值为其构造函数的prototype属性值
* 程序员能直接操作显式原型, 但不能直接操作隐式原型(ES6之前)
-->
<script>
// 定义构造函数
function Fun(){ // 内部语句:this.prototype = {}
}
// 1. 每个函数function都有一个prototype属性,即显式原型(属性) 默认指向一个空object对象
console.log(Fun.prototype); // Object
// 2. 每个实例对象都有一个__proto__属性,即隐式原型(属性)
// 创建实例对象
var fun = new Fun() // 内部语句:this.__proto__ = Fun.prototype
console.log(fun.__proto__); // Object
// 3. 实例对象的隐式原型的值为其对应构造函数的显式原型的值
console.log(fun.__proto__ === Fun.prototype); // true
// 4. 给原型添加方法
Fun.prototype.test = function(){
console.log("test()"); // test()
}
// 5. 通过实例对象调用原型的方法
fun.test();
// 6. 证明:如果一个对象找不到本身的属性,那么会寻找构造这个对象的属性;
// 也就是说找不到就去与其对应构造函数的显式原型找,例子如下
function Person(name, age){
this.name = name;
this.age = age;
this.skill = function () {
console.log(name + "现在" + age + "岁")
}
}
var zhongjie = new Person("中介", 22);
zhongjie.skill(); // 中介现在22岁
zhongjie.skill = function(){
console.log("波妞")
}
zhongjie.skill(); // 波妞
</script>
叁 ❀ 原型链
每一个引用类型都有__proto__,每一个函数都有prototype,引用类型的__proto__与它构造函数的prototype指向同一个对象;调用引用类型时,如果其本身并没有调用的方法或属性,就会去__proto__(也就是它构造函数的prototype)去找,没有的话会继续找prototype的__proto__,到顶级Object的原型指向null为止;
原型对象中有一个属性constructor, 它指向构造函数。
![](https://img-blog.csdnimg.cn/7cec1a99ca704cd19a030a91f857a925.png)
<script>
function Fun(){
this.test1 = function(){
console.log("test1()");
}
}
// 给原型对象添加方法(实例对象可以访问)
Fun.prototype.test2 = function(){
console.log("test2()");
}
console.log(Fun.prototype.__proto__); // object
var fun = new Fun();
console.log(fun);
fun.test1(); // test1()
fun.test2(); // test2()
console.log(fun.toString()); // [object Object]
fun.test3(); // fun.test3 is not a function
</script>
原型链的顶层就是Object.prototype,而Object的原型对象的是没有原型属性的。
Object.prototype._proto_ ===null
** **
❀加餐❀ ---- 加深原型链的理解
** 在JavaScript中万物都是对象,对象和对象之间也有关系,并不是孤立存在的。对象之间的继承关系,在JavaScript中是通过prototype对象指向父类对象,直到指向Object对象为止,这样就形成了一个原型指向的链条,专业术语称之为原型链。**
举例说明:person → Person → Object ,普通人继承人类,人类继承对象类
当我们访问对象的一个属性或方法时,它会先在对象自身中寻找,如果有则直接使用,如果没有则会去原型对象中寻找,如果找到则直接使用。如果没有则去原型的原型中寻找,直到找到Object对象的原型,Object对象的原型没有原型,如果在Object原型中依然没有找到,则返回undefined。
我们可以使用对象的hasOwnProperty()来检查对象自身中是否含有该属性;使用in检查对象中是否含有某个属性时,如果对象中没有但是原型中有,也会返回true
function Person() {}
Person.prototype.a = 123;
Person.prototype.sayHello = function () {
alert("hello");
};
var person = new Person()
console.log(person.a)//123
console.log(person.hasOwnProperty('a'));//false
console.log('a'in person)//true
<script>
function Fun(){
this.test1 = function(){
console.log("test1()");
}
}
// 给原型对象添加方法(实例对象可以访问)
Fun.prototype.test2 = function(){
console.log("test2()");
}
console.log(Fun.prototype.__proto__); // object
var fun = new Fun();
console.log(fun);
fun.test1(); // test1()
fun.test2(); // test2()
console.log(fun.toString()); // [object Object]
fun.test3(); // fun.test3 is not a function
</script>
person实例中没有a这个属性,从 person 对象中找不到 a 属性就会从 person 的原型也就是
person.__proto__
,也就是 Person.prototype中查找,很幸运地得到a的值为123。那假如
person.__proto__
中也没有该属性,又该如何查找?
当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性,如果还查不到,就去找原型的原型,一直找到最顶层Object为止。Object是JS中所有对象数据类型的基类(最顶层的类),在Object.prototype上没有
__proto__
这个属性。
console.log(Object.prototype.__proto__ === null) // true
所谓原型链,指的就是图中的proto这一条指针链!
原型链的顶层就是Object.prototype,而Object的原型对象的是没有原型属性的。
**先找自身,找不到就沿着
__proto__
一直往上找原型,直到找到最顶层的类Object,Object类没有原型了,如果还找不到就返回undefined**
版权归原作者 前端菜菜DayDayUp 所有, 如有侵权,请联系我们删除。