JavaScript-Advanced Days3 Notes

1、解释下如下代码,尤其是关于prototype的概念和使用。 最后介绍下为什么下面会提到用到prototype能够节约内存?

  <script>
    // 构造函数  公共的属性和方法 封装到 Star 构造函数里面了
    // 1.公共的属性写到 构造函数里面
    function Star(uname, age) {
      this.uname = uname
      this.age = age 
      this.sing = function () {
        console.log(`${uname}'唱歌'`)
      } 
    } 
    // 2. 公共的方法写到原型对象身上   节约了内存
    Star.prototype.sing = function () {
      console.log('唱歌')
    } 
    const ldh = new Star('刘德华', 55)
    const zxy = new Star('张学友', 58)
    ldh.sing() //调用
    zxy.sing() //调用 
    // console.log(ldh === zxy)  // false
    console.log(ldh.sing === zxy.sing) // false

    console.dir(Star.prototype) 
  </script>

这段代码是JavaScript中的一个示例,展示了如何使用构造函数和原型对象来创建对象。下面我将逐一解释代码中的关键概念和作用。

构造函数(Constructor)

构造函数是一种特殊的函数,用于创建和初始化对象。在JavaScript中,当你使用new关键字调用一个函数时,这个函数就被视为构造函数。构造函数通常以大写字母开头,以区分于普通函数。

在这个例子中,Star是一个构造函数,用于创建明星对象。它接受两个参数:uname(名字)和age(年龄),并将它们作为对象的属性。

function Star(uname, age) {
  this.uname = uname;
  this.age = age;
  this.sing = function () {
    console.log(`${uname}'唱歌'`);
  };
}

原型对象(Prototype)

每个JavaScript对象都有一个原型对象,它是一个包含所有可继承属性的对象。当你尝试访问一个对象的属性或方法时,如果该对象本身没有这个属性或方法,JavaScript引擎会沿着原型链向上查找,直到找到该属性或方法或者到达原型链的末端。

在这个例子中,Star.prototype是所有Star构造函数创建的对象共享的原型对象。通过将方法添加到原型对象上,所有实例都可以共享这些方法,而不需要在每个实例中重复定义。

Star.prototype.sing = function () {
  console.log('唱歌');
};

通俗解释

  • 构造函数:就像一个工厂,用来制造具有相同特征(属性和方法)的对象。
  • 原型对象:就像一个公共仓库,存储所有对象可以共享的属性和方法。

举例说明

假设我们有一个Car构造函数,用于创建汽车对象。每个汽车对象都需要有startstop方法。

我们可以将这些方法定义在Car.prototype上,这样所有汽车对象都可以共享这些方法,而不需要为每个汽车对象单独定义。

为什么使用原型可以节约内存?

在JavaScript中,对象是引用类型,意味着它们在内存中占用的空间比基本数据类型(如数字、字符串)要大。如果每个对象都独立存储自己的方法,那么每个对象都会占用更多的内存。通过将方法定义在原型对象上,所有对象实例都可以共享这些方法,从而减少了内存的使用。

在这个例子中,如果sing方法定义在每个Star实例中,那么每个Star对象都会占用额外的内存来存储这个方法。但是,通过将sing方法定义在Star.prototype上,所有Star实例都共享同一个sing方法,从而节约了内存。

代码中的输出

  • ldh.sing() 和 zxy.sing() 都会调用原型对象上的sing方法,输出"唱歌"。
  • console.log(ldh.sing === zxy.sing) 会输出false,因为尽管两个对象共享同一个sing方法,但是它们各自的sing属性是通过构造函数中的this.sing赋值的,所以它们指向的是不同的函数。

最后,console.dir(Star.prototype) 会输出Star原型对象的所有属性和方法,包括sing方法。

 

2、为什么如下代码的reduce前面一定得要加this呢?不加不行吗?

    const arr = [1, 2, 3]
    Array.prototype.max = function () {
        return Math.max(...arr)
    }

    Array.prototype.min = function () {
        return min.max(...arr)
    }


    Array.prototype.sum = function (){
        return this.reduce((prev, item) => prev + item, 0) # 这里
    }

    console.log([1, 2, 3].sum())
    console.log([11, 21, 31].sum())

reduce前面必须加上this原因是 this 代表当前调用数组实例的上下文,也就是代码中的 [1, 2, 3][11, 21, 31]。在 Array.prototype.sum 函数里,如果你不使用 this,JavaScript 将不知道你要对哪个数组执行 reduce 操作。

来看具体的原因:

为什么需要 this

  • 当前上下文: Array.prototype.sum 方法中,this 指代调用该方法的数组实例,比如 [1, 2, 3]。也就是说,当你调用 [1, 2, 3].sum() 时,this 就是 [1, 2, 3]

  • reduce 的作用: reduce 是一个数组的方法,用于将数组中的元素累积为一个值。如果不加 this,代码将不知道你要对哪个数组进行累积操作。而加上 this 后,它明确告诉 JavaScript 应该在当前数组实例上执行 reduce

如果不加 this 会发生什么?

如果你不加 this,则代码找不到一个有效的数组来调用 reduce,会导致报错:

Array.prototype.sum = function () {
    return reduce((prev, item) => prev + item, 0);
}

上述代码会导致一个 ReferenceError 错误,因为 reduce 是数组实例的方法,而你没有指定要对哪个数组调用它。

正确的写法:

Array.prototype.sum = function () {
    return this.reduce((prev, item) => prev + item, 0);
}

这里的 this.reduce 确保 reduce 是在当前数组实例上执行的,正确累加数组中的元素。

 

3、关于原型里的constructor属性理解

个人理解:XX.constructor就理解为 XX的构造者,一般都是构造函数

 

 

4、如何理解区分原型对象对象原型

个人理解:

找定语,原型对象prototype是从构造函数里面去找, 是指构造函数的prototype属性,它是一个对象,定义了由该构造函数创建的实例所共享的属性和方法。

         对象原型是从实例身上去找;

 

"原型对象"(Prototype Object)和"对象原型"(Prototype of Object)虽然听起来相似,但它们在JavaScript中有不同的含义。

为了帮助理解这两个概念,下面我会通俗地解释它们,并通过简单的例子来说明区别。

1. 原型对象(Prototype Object)

概念:

  • 原型对象是指构造函数的prototype属性,它是一个对象,定义了由该构造函数创建的实例所共享的属性和方法。
  • 换句话说,当使用构造函数创建一个新对象时,该对象会自动继承构造函数的原型对象中的属性和方法。

通俗解释:

  • 可以把原型对象想象成一张“蓝图”或者“模板”。每当你用构造函数创建一个新对象时,这个新对象都会根据这张蓝图去继承一些属性和方法。

举例说明:

// 创建一个构造函数
function Person(name) {
  this.name = name;
}

// 给构造函数的原型对象添加一个方法
Person.prototype.sayHello = function() {
  console.log('Hello, my name is ' + this.name);
};

// 用构造函数创建一个实例
const person1 = new Person('Alice');

// 实例person1会继承原型对象中的方法
person1.sayHello(); // 输出:Hello, my name is Alice

在这个例子中:

  • Person.prototype 就是构造函数 Person 的原型对象,它定义了 sayHello 方法。
  • 当我们用 new Person() 创建实例 person1 时,person1 就会从 Person.prototype 继承这个方法。

 

2. 对象原型(Prototype of Object)

概念:

  • 对象原型指任何对象内部的一个隐式属性 [[Prototype]],它指向该对象的“原型”,即从哪个对象继承属性和方法。
  • 在 JavaScript 中,我们可以通过 Object.getPrototypeOf(obj) 或者 obj.__proto__ 来访问某个对象的原型(也就是它的 [[Prototype]])。

通俗解释:

  • 每个对象都有一个“幕后导师”或者“父母”,这个幕后导师就是对象的原型。对象会从它的原型那里继承属性和方法,就像孩子从父母那里继承一些特质一样。
  • 对象原型就是对象内部的隐藏链接,它指向另外一个对象(原型对象),这个对象提供继承的功能。

举例说明:

const obj = { a: 1 };

// 获取obj的原型(即对象原型)
console.log(Object.getPrototypeOf(obj)); // 输出:{},默认是Object.prototype

// 定义一个对象,且指定其原型
const newObj = Object.create(obj);
console.log(newObj.a); // 输出:1, newObj从obj继承了属性a

在这个例子中:

  • obj 的原型是 Object.prototype,即所有对象都默认继承的原型
  • newObj 是通过 Object.create(obj) 创建的,newObj 的原型是 obj,所以它可以访问 obj 中的属性 a

区别总结:

  • **原型对象(Prototype Object)**是构造函数的 prototype 属性,它定义了通过构造函数创建的实例对象所共享的属性和方法。所有实例通过构造函数的原型对象进行继承。
  • **对象原型(Prototype of Object)**是指某个对象的[[Prototype]],即它从哪个对象继承而来。对象的原型是指向另一个对象(可能是构造函数的原型对象),这决定了该对象能访问哪些继承的属性和方法。

总结类比:

  • 原型对象:是一张蓝图,所有用同一个构造函数创建的对象都依据这张蓝图来“继承”属性和方法。
  • 对象原型是每个对象内部的一个隐式“链”,告诉这个对象它的继承来源是谁,这个来源通常是构造函数的原型对象。
  • 对象原型: 是某个具体对象的内部属性,指向其继承的对象,常用 Object.getPrototypeOf() 来获取。
  • 原型对象:是构造函数的属性,决定了由这个构造函数创建的对象共享的属性和方法。

这就是对象原型和原型对象的区别,你问的“对象原型”实际上是 [[Prototype]],它是每个对象内部的一个属性,指向该对象的原型链上的下一个对象。

 

6、对象原型([[Prototype]]):是每个 JavaScript 对象(包括实例和函数对象)都有的内部属性,它指向该对象的原型。 那么请问什么是 原型

原型(Prototype) 是 JavaScript 中用来实现对象继承的一种机制。它本质上是一个对象,作用是为其他对象提供属性和方法。通过原型机制,JavaScript 实现了对象的属性继承,从而能够共享和复用代码。

在每个 JavaScript 对象(除了 null)内部都有一个指向原型对象的隐藏属性,叫做 [[Prototype]](即对象原型)。这个原型本质上就是一个普通的对象,定义了所有通过该原型继承的对象可以访问的属性和方法。

原型的角色

  • 原型 是用来实现继承的。JavaScript 中的每个对象都有一个原型对象,它通过原型链(Prototype Chain)来实现属性的查找和继承。
  • 当我们访问一个对象的属性时,JavaScript 引擎首先检查对象自身是否有这个属性。如果没有,它会沿着对象的原型链向上查找,直到找到该属性或者查找到 null 为止。

原型对象和对象原型

  • **原型对象(Prototype Object):**是构造函数的一个属性,用来为所有该构造函数创建的实例对象提供共享的属性和方法。
  • **对象原型([[Prototype]]):**是每个对象的一个隐藏属性,指向该对象的原型,原型就是另一个对象。我们可以通过 Object.getPrototypeOf(obj)obj.__proto__ 来获取。

举个例子:

// 定义一个构造函数
function Person(name) {
  this.name = name;
}

// 向构造函数的原型对象上添加方法
Person.prototype.sayHello = function() {
  console.log('Hello, my name is ' + this.name);
};

const alice = new Person('Alice');

// 当访问 alice.sayHello 时,JavaScript 会首先检查 alice 对象本身是否有 sayHello 属性。
// 如果没有,它会通过 alice 的 [[Prototype]] (即 Person.prototype)去查找。
alice.sayHello(); // 输出:Hello, my name is Alice

// 获取 alice 对象的原型
console.log(Object.getPrototypeOf(alice) === Person.prototype); // 输出:true

// 通过 Person.prototype 共享方法
const bob = new Person('Bob');
bob.sayHello(); // 输出:Hello, my name is Bob

什么是原型?

在上面的例子中:

  • Person.prototype 就是原型对象,它为所有 Person 的实例(例如 alicebob)提供共享的属性和方法。
  • alice.__proto__ 或者 Object.getPrototypeOf(alice)alice 对象的 [[Prototype]],它指向 Person.prototype

因此,原型可以理解为一个对象(例如 Person.prototype),为其他对象提供属性和方法。每个对象都通过其 [[Prototype]] 属性与原型对象相关联,形成继承链。

原型链

多个对象的原型可以形成一条“链”,这就是原型链。

通过这条链,当我们访问一个对象的属性时,如果这个对象本身没有这个属性,JavaScript 会沿着原型链逐层查找,直到找到该属性或返回 undefined 为止。

console.log(alice.__proto__);            // Person.prototype
console.log(alice.__proto__.__proto__);   // Object.prototype
console.log(alice.__proto__.__proto__.__proto__);  // null (Object.prototype 的原型是 null)

关键点

  • 原型对象 是一个对象,通常是通过构造函数的 prototype 属性创建的,它为实例对象提供共享属性和方法。
  • 对象原型([[Prototype]] 是每个对象的内部属性,指向它的原型对象,从而允许对象继承该原型上的属性和方法。

 

posted @ 2024-09-18 22:27  AlphaGeek  阅读(15)  评论(0)    收藏  举报