JavaScript中的原型(Prototype)机制是其面向对象编程的核心特性之一,它允许对象之间共享属性和方法,从而实现继承。本文将从浅入深地解析原型、原型链、以及一些特殊情况下的应用,旨在让你对这一概念有更清晰的理解。
原型(显示原型)
每个JavaScript函数都有一个名为prototype
的属性,这是一个对象,用于存储所有通过该函数构造出的对象所共享的属性和方法。当使用new
关键字基于某个构造函数创建一个实例化对象时,这个实例化对象会自动链接到构造函数的prototype
对象上,这便是原型继承的基础。而该原型指的是函数的原型prototype
。
function Person() {}
Person.prototype.say = function() { console.log('Hello'); };
在这个例子中,所有通过new Person()
创建的实例化对象都会继承构造函数的原型上的say
方法。
隐式原型和原型链
每个JavaScript对象(除null
外)都有一个内部属性[[Prototype]]
,通常可以通过__proto__
访问(隐式原型__proto__
指向的是对象的显示原型prototype
)。当访问一个对象的属性或方法时,如果对象本身没有,则引擎会继续在其[[Prototype]]
指向的对象中寻找,这一过程形成了一条链,即原型链。
对于p
来说,其原型链上首先查找自身的属性,若未找到,则会沿着p.__proto__ -> Person.prototype -> Object.prototype -> null
这条链进行查找。
也就是说,v8在查找对象的属性时,如果没找到,就会顺着对象的隐式原型往上查找,如果还找不到,再顺着隐式原型的隐式原型往上找,直到找到null为止,在这个过程中,但凡有一个步骤能找到,就会返回值。这个链状的查找过程称为原型链
是不是所有的对象都有原型??
答案当然是,并非所有对象都有原型。使用Object.create(null)
可以创建一个没有原型(__proto__
会指向null
)的对象,这样的对象不会继承任何默认的Object方法。
修改原型的属性
实例化对象是不可以修改原型上的属性的
Person.prototype.like = '听歌';
let p = new Person();
p.like = '撸铁';
let s = new Person()
console.log(p)
console.log(s.like)
由后面实例化出来的s
对象中可以看出,当执行p.like
的时候,并不是修改原型上的属性,而是在p
对象上添加一个like
属性并赋值为撸铁
。
多级继承示例
多级继承展示了如何通过原型链实现深层次的属性和方法继承,每个子类通过其原型链连接到父类的原型。
Grand.prototype.lastname = '张';
function Grand(){
this.name='三'
}
Father.prototype=new Grand()
function Father(){
this.age=40
}
Son.prototype=new Father()
function Son(){
this.like='coding'
}
let son = new Son();
// console.log(son.like)
// console.log(son.age)
console.log(son.name)
下面我将描述出输出son.name
时的查找路线:
原型链的实践与注意事项
修改数组的push
方法展示了原型链上方法覆盖的原理。
// var arr=[1,2,3]
// arr.push(4)//1,2,3,4
// 原型链
Array.prototype.push = function(){
this[0]='a'
}
var arr=[1,2,3] // new Array() --> this
arr.push(4)
console.log(arr)//4,2,3
Number.prototype.toString
等内建原型方法的调用,说明了即使是最基本的数据类型也是基于原型链实现方法访问的。
var num=1// new Number()
console.log(num.toString())
构造函数与原型对象的区分
构造函数本身也是一个对象,它有自己的属性和方法,这些不通过原型链传递给实例。例如,Foo.b=2
设置的是构造函数的静态属性,而非原型属性。
Foo.prototype.a=1
function Foo() {
// this={
// }
// this.__proto__ = Foo.prototype
// return this
// b=2
}
Foo.b=2
console.log(Foo);//{b:2}
let f = new Foo();
console.log(f.b);//undefined
最后
总的来说,JavaScript的原型和原型链机制提供了一种灵活而强大的对象继承方式。理解这一机制对于深入掌握JavaScript的面向对象编程至关重要。通过上述示例的逐步分析,我们不仅看到了原型如何让对象共享属性和方法,还见识到了原型链如何构建起对象间属性查找的路径。这将会使我们更好的理解为什么可以调用不属于他身上的属性和方法,便于我们更好的掌握这一门编程语言!!
作者:常乐hhh
链接:https://juejin.cn/post/7379897208532926518
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
该文章在 2024/6/17 16:32:31 编辑过