首先,让我们看一段代码:
let str1='aabbccdd'
let arr=str1.split("")
let str2=arr.join("-")
代码内容非常简单,将str1分割成数组arr,再将arr的每个元素通过“-”拼接成字符串,若console.log打印得到的结果为''a-a-b-b-c-c-d-d''。
在这里,我就提出一个问题,str1.split("")中的split()方法和arr.join("")中的join()方法是从哪来的呢?这个问题在下方会给出答案。
我们平常创建数组、字符串时一般都是不是都是像下面一样定义一个变量/常量,这种创建方式被称之为语法糖,而在早期JavaScript开发者则是构造一个Array的实例,向Array函数(Array是一个JavaScript内置构造函数)括号中加入数组元素(let arr = new Array() ),
let arr=[1,2,3,4,5]
既然提到了构造函数了,那不得不提到new操作符。构造函数通过new操作符,将实例内部的__proto__特性赋值为构造函数的prototype属性。
function Person(){
//函数体
}
let person1 = new Person();
console.log(person1.__proto__ == Person.prototype) //true
什么是prototype?
一个函数被创建后都会拥有一个prototype属性,这个属性是一个对象,这个对象包含了应该由特定的引用类型实例共享的属性和方法。这个对象就是通过调用构造函数创建的对象的原型。那这个原型对象有什么用呢?
function Person(){
Person.prototype.age = 20;
Person.prototype.sayAge = function(){
console.log(this.age);
}
}
let person1 = new Person();
let person2 = new Person();
person1.sayAge(); //20
person2.sayAge(); //20
console.log(person1.age); //20
console.log(person1.age); //20
console.log(person1.age == person2.age) //true
通过例子我们可以看出原型对象上定义的属性和方法都可以被实例对象共享 。且在构造函数中直接赋予对象实例的值,能被直接赋值在他们的原型上。
如何理解原型?
前面说过,创建函数就会使这个函数拥有一个prototype属性(指向原型对象)。默认情况下,所有原型对象会自动获得一个名为constructor的属性,这个属性将指回与之关联的构造函数。
function Person (){
//函数体
}
let person1 = new Person()
console.log(person1.__proto__ == Person.prototype); //true
console.log(Person.prototype.constructor == Person); //true
console.log(person1.__proto__.constructor == Person); //true
//person1.__proto__与Person没有直接联系,是因为
//person1.__proto__ == Person.prototype才使得
//person1.__proto__与Person有联系
这里需要注意:实例对象与构造函数原型之间有直接联系,但实例与构造函数之间则不存在直接联系。这里用一幅图来表示
上图展示了,构造函数,原型对象和实力之间的关系。这里Person.prototype指向原型对象,而Person.prototype.constructor指回构造函数,实例只有一个内部属性指回Person.prototype。
什么是原型链?
前面说道,每个构造函数都有一个原型对象,原型有一个指针指回构造函数,而实例有一个内部指针指向原型。那如果原型是另一个类型的实例呢?那就意味着这个原型本身又一个内部指针指向另一个原型,而另一个原型也有一个指针指向另一个构造函数。这样就在实例和原型之间形成链式结构,被称为原型链。这里就利用上图来解释一下。
//上图中person1的原型链如下:
person1.__proto__ == Person.prototype;
person1.__proto__.__proto__== OBject.prototype
person1.__proto__.__proto__.__proto__== null