一分钟了解原型对象
js分为函数对象和普通对象 ,每个对象都有__proto__属性,但是只有函数对象才有prototype属性,prototype属性就是函数的原型对象。
比如说 构造函数通过new 实化一个实例对象,实例对象的__proto__ 指向原型对象 ,同时构造函数prototype也指向原型对象。
比如:
function Person(name, age) { this.name = name; this.age = age; } Person.prototype.sayHello = function() { console.log("Hello, my name is " + this.name + " and I am " + this.age + " years old."); } var person1 = new Person("Alice", 25); var person2 = new Person("Bob", 30); person1.sayHello(); // 输出:Hello, my name is Alice and I am 25 years old. person2.sayHello(); // 输出:Hello, my name is Bob and I am 30 years old.
Person是一个函数,也是构造函数,他的prototype就是指向原型对象。我们使用 Person.prototype 对象来为每个 Person 对象定义了一个共同的方法 sayHello。这个方法输出了一个字符串,表示人物的名字和年龄。
我们使用 new 关键字来创建了两个 Person 对象,并且分别调用了 sayHello 方法,输出了不同的字符串。这是因为 person1 和 person2 都继承了 Person.prototype 对象上的 sayHello 方法,但是它们的属性值是不同的。
原型对象到底是什么?
可以将原型对象比做是一个模板,这个模板包含了一些常用属性和方法,然后我们可以用它来创建新的对象。就像是我们可以用一个模板来快速制作许多同样的产品一样,每个产品的具体细节可能不同,但是它们的基本结构和特征都是相同的。
比如说,我们可以有一个“车”的原型对象,它包含了一些常用的属性和方法,比如车型、品牌、最高时速、加速度等等。然后我们可以用这个原型对象来创建许多不同的车型,如轿车、卡车、摩托车等等。由于这些车型都基于同一个原型对象创建,因此它们都具有相同的基本特征和结构,但是具体的细节可能略有不同,比如轿车和卡车的尺寸、载重等等。
同样地,在JavaScript中,我们可以使用原型对象来快速创建多个对象,并且这些对象都具有相同的基本特征和方法。这种基于原型对象的方式可以实现代码的重用,提高开发效率。
原型对象能干什么
上面的例子已经说明,我们可以在函数的prototype熟悉,也就是原型对象上添加熟悉和方法,这些都会被将来根据这个函数new出来的对象继承。
原型对象在JavaScript中有着重要的作用,常常用于以下几个方面:
实现继承:通过原型对象,我们可以创建新的对象,并且继承原型对象上的属性和方法。这种基于原型的继承方式可以让我们更方便地实现面向对象编程。
共享属性和方法:通过将属性和方法定义在原型对象上,我们可以让多个对象共享这些属性和方法。这样可以减少内存占用,提高代码的效率。
扩展对象:通过修改原型对象,我们可以在运行时扩展对象的属性和方法。这种动态扩展对象的方式可以让我们更加灵活地处理对象。
实现一些常用的方法和功能:原型对象上已经包含了一些常用的方法和功能,比如 toString、valueOf 等等。通过在原型对象上定义相应的方法,我们可以为对象实现这些常用方法,以便在开发过程中更加便捷地使用。
举个栗子:
function Person() {} Person.prototype.sayHello = function() { console.log("Hello, I'm a person."); } function Student() {} Student.prototype = Object.create(Person.prototype); const alice = new Student(); alice.sayHello(); // 输出:Hello, I'm a person.
在这个例子中,我们通过原型链访问了alice对象的原型对象,然后通过它的原型对象调用了sayHello方法。由于Student函数的原型对象继承自Person函数的原型对象,因此alice对象也继承了Person函数的原型对象上的方法和属性。
proto
__proto__是JavaScript中的一个内置属性,它指向对象的原型。在JavaScript中,每个对象都有一个原型对象,这个原型对象包含了对象的所有属性和方法,用来定义对象的共同属性和方法。__proto__属性就是用来表示对象的原型的。
例如,我们可以使用以下代码来创建一个简单的对象,然后查看其__proto__属性:
const person = { name: "Alice", age: 25 }; console.log(person.__proto__); // 输出:Object {}
上面的代码中,我们定义了一个名为person的对象,它有两个属性:name和age。然后,我们使用console.log()方法输出了person.__proto__属性,这个属性指向的是这个对象的原型对象,也就是Object对象。
需要注意的是,__proto__是一个非标准的属性,在ECMAScript 2015 标准中已经不推荐使用。应该使用Object.getPrototypeOf()方法或Object.setPrototypeOf()方法来操作对象的原型。例如,我们可以使用以下方法来获取一个对象的原型:
const person = { name: "Alice", age: 25 }; console.log(Object.getPrototypeOf(person)); // 输出:Object {}
上面的代码中,我们使用Object.getPrototypeOf()方法来获取person对象的原型,它返回的是一个Object对象。
__proto__有什么作用
查看对象的原型
我们可以使用__proto__访问一个对象的原型,以便了解它的继承关系。例如:
function Person() {} const alice = new Person(); console.log(alice.__proto__); // 输出:Person {}
在这个例子中,我们创建了一个Person函数并使用它创建了一个alice对象。然后,通过alice.__proto__访问了alice对象的原型,这个原型就是Person函数的原型对象。
创建对象的原型链
我们可以使用__proto__来创建对象的原型链。例如:
function Person() {} const alice = { name: "Alice" }; alice.__proto__ = Person.prototype; console.log(alice instanceof Person); // 输出:true
在这个例子中,我们定义了一个空对象alice,然后将它的原型链指向了Person函数的原型对象。由于alice对象的原型链继承了Person函数的原型,因此它可以被认为是Person类型的实例。
修改对象的原型
我们可以使用__proto__来动态地修改一个对象的原型对象。例如:
function Person() {} const alice = new Person(); alice.__proto__ = { sayHello() { console.log("Hello, I'm a new object."); } }; alice.sayHello(); // 输出:Hello, I'm a new object.
在这个例子中,我们创建了一个Person对象alice,然后通过alice.__proto__动态地修改了它的原型对象,将它的原型对象替换成一个新的对象。最后,调用alice.sayHello()方法时输出了Hello, I'm a new object.,说明alice对象的原型已经被成功替换。
原型链
当试图得到一个对象的属性时,如果这个对象本身不存在这个属性,那么就会去它的’_ _ proto_ _'属性(也就是它的构造函数的’prototype’属性)中去寻找。这个寻找的链路就是原型链。
看一个例子:
// 构造函数 function Foo(name,age){ this.name=name; this.age=age; } Object.prototype.toString=function(){ //this是什么要看执行的时候谁调用了这个函数。 console.log("I'm "+this.name+" And I'm "+this.age); } var fn=new Foo('小明',19); fn.toString(); //I'm 小明 And I'm 19 console.log(fn.toString===Foo.prototype.__proto__.toString); //true console.log(fn.__proto__ ===Foo.prototype)//true console.log(Foo.prototype.__proto__===Object.prototype)//true console.log(Object.prototype.__proto__===null)//true
来个图帮助理解。
总结
1、所有的引用类型(数组、函数、对象)都可以通过原型可以自由扩展属性(除null以外)。
2、所有的引用类型都有一个’_ _ proto_ _'属性(也叫隐式原型,它是一个普通的对象)。
3、所有的函数都有一个’prototype’属性(这也叫显式原型,它也是一个普通的对象)。
4、所有引用类型,它的’_ _ proto_ _'属性指向它的构造函数的’prototype’属性。(结合2和3)
5、当试图得到一个对象的属性时,如果这个对象本身不存在这个属性,那么就会去它的’_ _ proto_ _'属性(也就是它的构造函数的’prototype’属性)中去寻找。这就是原型链。