原型(prototype)
所有的 JavaScript 对象都会从一个 prototype(原型对象)中继承属性和方法。
我们所创建的每一个函数,解析器都会向函数中添加一个属性prototype
,这个属性对应着一个对象,这个对象就是我们所谓的原型对象
- 如果函数作为普通函数调用
prototype
没有任何作用
- 当函数以构造函数的形式调用时,它所创建的对象中都会有一个隐含的属性,指向该构造函数的原型对象,我们可以通过
__proto__
来访问该属性。
原型对象原理
在如下图所示中,创建一个构造函数Person,这个构造函数中有一个隐含的属性prototype,这个属性指向的对象就是原型对象,它的内存地址假设为0x123,则该构造函数对应的prototype属性则指向这个内存地址,随后通过构造函数创建一个实例per,我们可以使用__proto__来访问构造函数中的原型对象,在该实例中添加一个name属性,同时给原型对象中也添加一个属性age,这两个属性的区别就是name属性为per实例私有的,而age属性则是共有的,即使再创建一个per2实例,没有给它添加age属性,per2.age也是可以访问到原型对象中的age属性的,正如在控制台输出per.age一样,可以正常访问到原型对象中的age属性的。
向原型对象中添加的属性为公共属性,通过构造函数所创建的实例都可以访问到对应原型对象中添加的属性。
上期性能优化
上期我们说到使用构造函数创建对象中,在构造函数内创建sayName方法,每创建一个实例就会重新创建一个sayName方法,大大占用了我们的内存,之后我们将该对象的方法提取了出来,在全局作用域中重新定义了一个函数,如图:
这种方法虽然解决了内存占用的问题,但也存在一种缺陷,因为它时在全局作用域中定义,并且这个函数只用在该构造函数中,污染了全局作用域的命名空间,再开发过程中,他人再创建相同名字的函数时就会覆盖该函数,此时再通过该构造函数调用该方法时,性质就完全改变了。因此我们需要在该构造函数的原型对象中添加该方法,这样即不会占用大量内存,该构造函数创建的实例都可以访问到该方法,也不会污染全局作用域的命名空间。在开发过程中也相对安全。下面我们一起看看如何修改吧。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script> function Person(name,age,gender){ this.name = name ; this.age = age ; this.gender = gender; } Person.prototype.sayName = function(){ console.log(this.name) } var per = new Person('suliang',21,'男'); var per1 = new Person('小红',18,'女'); console.log(per.sayName == per1.sayName); per.sayName(); per1.sayName(); </script> </head> <body> </body> </html>
运行结果:
in操作符和hasOwnProperty方法的使用
使用in
来检查一个对象是否包含某个属性时,如果对象中没有但是该对象的原型中有,也会返回true。
而用hasOwnProperty
方法时,则是检查本身对象是否含有某个属性。只有本身对象含有该属性才会返回true。
原型对象也是个对象,它本身也有原型对象,最后的原型就是Object。即到Object就没有原型了。
hasOwnProperty
这个方法则是在该构造函数的原型的原型对象中。