函数也是一个对象,当真正开始执行函数,执行环境会为函数分配2个空间:
函数对象变量空间、函数对象空间
函数对象变量空间:存在栈中,用函数名表示。
函数对象空间:存在堆中,开辟一个内存空间,这空间中有个默认的prototype属性(它就是原型对象属性,指向原型对象空间)
function funa() {
console.log("this is funa");
}
funa(); //this is funa
console.log("funa.name:", funa.name); // 栈中 funa
console.log("funa.prototype:", funa.prototype); //堆中 {constructor: ƒ}
console.log("funa.__proto__:", funa.__proto__); //堆中 ƒ () { [native code] }
image.png
函数 和 构造函数的区别
当通过 new 函数()时,此刻这个函数就是构造函数
当执行环境执行 new 构造函数时,构造函数中通过 this 定义的属性和方法会分配到,这个实例对象空间中
构造函数所有实例对象都可以访问 原型对象空间上的属性和方法。
function people(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
//实例对象 <= 构造函数
const a = new people("小a", 18, "女");
const b = new people("小b", 19, "男");
console.log('a实例对象:', a); //people {name: '小a', age: 18, sex: '女'}
console.log('a.name:', a.name); //小a
console.log('a.__proto__:', a.__proto__); //{constructor: ƒ}
console.log('a.prototype:', a.prototype); //undefined //没有这个属性
console.log('people构造函数:', people()); //空 因为没return
console.log('people.prototype:', people.prototype); // {constructor: ƒ}
console.log('people.__proto__:', people.__proto__); // ƒ () { [native code] }
为什么要把属性和方法 放 原型对象上
下面的示例可以看出,100个实例对象,它们的job都是一样的,放到自己的对象空间上,浪费大量内存空间
所以把相同的属性方法放到原型对象上,这样就节约内存空间
function people1(name, age, sex) {
this.name = name
this.age = age
this.sex = sex
this.job = () => {
console.log(this.name + '执行job()');
}
}
const a1 = new people1("小a", 18, "女")
const b1 = new people1("小b", 19, "男")
a1.job() //小a执行job()
b1.job() //小b执行job()
//把相同的属性方法放到原型对象上,节约内存空间也便于统一管理
people1.prototype.walk = function () {
console.log(this.name + '执行walk()');
}
a1.walk() //小a执行walk()
b1.walk() //小b执行walk()
image.png
如何访问 原型对象空间中属性和方法?
实例对象 访问 原型对象空间
每一个实例都有默认的 __proto__
属性,它指向 构造函数的 原型对象空间
console.log('a.__proto__:', a.__proto__); //{constructor: ƒ} //原型对象空间
构造函数 访问 原型对象空间
可以直接通过 构造函数.prototype
对象属性来访问原型对象空间上的属性和方法
console.log('people.prototype:', people.prototype); // {constructor: ƒ}
增加或修改原型对象的属性或方法后,所有的实例对象立即可以访问到(但创建实例后再覆盖原型除外)
people.prototype.say = function () {
return 'people.say方法'
}
console.log('people.prototype.say()', people.prototype.say()) //people.say方法
console.log('a.say():', a.say()); //a.say(): people.say方法
// 覆盖原型对象的属性或方法后,所有的实例对象不能访问
people.prototype.run = function () {
return 'people.run方法'
}
const c = new people("小c", 20, "男")
console.log('c.run()-1:', c.run()); //c.run()-2: people.run方法
console.log('是否相等1:', c.__proto__ === people.prototype); //true
people.prototype = {
run: function () {
return 'people.run 覆盖后的方法'
}
}
//先实例 再覆盖原型,是访问不到新的,所以仍然是之前的原型
console.log('c.run()-2:', c.run()); //c.run()-2: people.run方法
//因为实例中的__proto__指向的是原有地址,而构造函数的prototype 已经指向别新的地址
//所以二者是不同的,可以看见,判断是否相等,上面是true,这里是false
console.log('是否相等2:', c.__proto__ === people.prototype); //false
const d = new people("小d", 21, "女")
console.log('d.run()-1:', d.run()); //d.run()-1: people.run 覆盖后的方法
console.log('是否相等3:', d.__proto__ === people.prototype); //true
image.png
实例对象访问属性和方法,访问顺序是如何的?
实例对象 访问属性和方法,首先从 自身实例对象空间 中查找。如果找到该属性和方法,就停止查找,表示找到了;
如果没有找到,就继续在该实例的原型对象空间中 去查找该属性和方法
console.log(a.say()); //先从 实例的对象空间中 查找,找不到然后到原型对象空间中 查找
网友评论