javascript 原型

2022-03-05  本文已影响0人  wyc0859

函数也是一个对象,当真正开始执行函数,执行环境会为函数分配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()); //先从 实例的对象空间中 查找,找不到然后到原型对象空间中 查找
上一篇下一篇

猜你喜欢

热点阅读