构造函数、原型、原型链
构造函数、原型、原型链
原型和原型链应该是前端的面试中,必定会问到的东西吧。但是这一块很多小伙伴们都会绕不明白,今天我们就一起来深入了解一下原型和原型链吧。
要说原型的话,我们就要先来知道构造函数是一个什么东西。
构造函数
定义:通过new
一个 函数名,来实例化对象的函数就叫做构造函数。
注意:构造函数在定义的时候,必须使用大驼峰命名方式,比如:一、构造函数和普通函数的区别CreateHuman
- 构造函数需要用
new
来创建,而普通函数不需要。构造函数: function Person() { this.name = '小柒' }; var p = new Person(); 普通函数 function person() {}; var p = person();
- this指向,在构造函数的内部,this指向的是构造出来的那个对象;而普通函数的this指向的是window全局对象。
构造函数: function Person() { this.name = '小柒' }; var p = new Person(); console.log(p.name); // 小柒 这个时候的this就是p普通函数: function person() { return this; }; var p = person(); console.log(p); // window
- 构造函数默认return this,也就是新的实例对象;而普通函数默认返回的是undefined;要是设置了return的值的话,那么返回值会根据return的值类型来决定了。
如果return的是五种简单的数据类型:
Number
、String
、Boolean
、Null
、Undefined
的话,构造函数还是返回this对象,而普通函数会返回return后面的值。
如果return的是引用类型的数据类型:Array
、Date
、Object
、Function
、RegExp
、Error
的话,构造函数和普通函数都会返回return后面的值。
- 系统自带的构造函数
new Objectt()
new Array()
new Number()
new Boolean()
new Date()
Object()
可以批量的生产出对象,每一个对象都一样,但是彼此之间是相互独立的
在Object()
前面加上一个new
,变成new Object()
的执行,通过return
返回一个真正的对象,然后在拿一个变量来接收。如:var a = new Object()
注:
var obj = {}
和var obj = new Object()
这样写区别不大
- 自定义的构造函数
Object.create(proto, [propertiesObject])
创建一个新的对象,使用现有的对象来提供新创建的对象proto
- 参数
- proto:必须,表示新建对象的原型对象,也就是说该参数会被赋值到目标对象的原型上。该参数可以是
null
、对象
、函数的prototype
属性。 > 注:创建空对象的时候,要填null
,否则会报类型错误。
- propertiesObject : 可选。 添加到新创建对象的可枚举属性,对象的属性描述符以及相应的属性名称。这些属性对应
Object.defineProperties()
的第二个参数。
- proto:必须,表示新建对象的原型对象,也就是说该参数会被赋值到目标对象的原型上。该参数可以是
- 参数
注: 接下来的前提必须是new之后的,而且下面的三步都是隐式的。
- 在函数体的最前面隐式的添加上
var this = {}
空对象(但这个不是空对象,里面有什么我在后面揭晓) - 执行
this.xxx = xxx
- 隐式的返回
return this
例子1:
function Student (name, age, sex) {
//一、隐式的创建this对象
// this = {
//name: " ",
//age: " ",
//sex: " ",
// }
//二、执行this.XXX = XXX;
this.name = name;
this.age = age;
this.sex = sex;
//三、隐式的返回return this;
}
var student1 = new Student('zhangsan', 20, 'male');
console.log(student1);
// {name: "zhangsan", age: 20, sex: "male"}例子2:
function Person (name, height) {
//隐式的var this = {}, 下面正常的执行this
this.name = name;
this.height = height;
this.say = function () {
console.log(this.say)//这里的this和外面的this不一样
}
//return this
}
console.log(new Person('xiaowang', 170).name);
//xiaowang
原型
说了这么久,终于讲到了原型,我们我们就来看看什么是原型吧。
定义:原型是利用原型可以做什么事呢?function
对象的一个属性,他定义了构造函数制造出的对象的公有祖先。通过该构造函数产生的对象,可以继承原型的属性和方法。并且原型也是对象噢~
利用原型的特点和概念, 可以提取共有属性看下面的这个例子你就知道是什么意思了
//第一个应用,在工厂里面生产每个都一样的时候,我们可以把共有的属性给提供出来,放到原型里面
function Car (color, owner) {
this.color = color;
this.owner = owner;
}
//Car.prototype 刚出生的时候就已经定义好了
Car.prototype = {
name: "BMW",
height: 1400,
lang : 4900
}
var a = new Car('red', 'prof.Ji');
// Car {color: "red", owner: "prof.Ji"}
var b = new Car('black', 'prof.Deng');
// Car {color: "black", owner: "prof.Deng"}
//看上去a和b的属性是这样的,但是真正要用的时候:
console.log(a.name);
//BMW
console.log(b.name);
//BMW
上面的这个例子,每个对象都会有一些一样的属性(我把这样的属性叫做工程化属性),我们把这些工程化属性放在原型里不是更好嘛。
一、原型的增、删、改、查
Person.prototype.LastName = 'Qi';
function Person(name) {
this.name = name;
}
var a = new Person('Xiao');
我们就根据这上面的代码当作增删改查的例子
- 查
Person.prototype
这样就会返回一个对象,里面有原型的所有属性
//返回结果 {LastName: "Qi", constructor: ?}
- 改
要是想在原型链上修改的话,比如说我们把Person.prototype.LastName
修改一下,需要这样Person.prototype.LastName = 'XiaoQi'
但要是a.LastName = 'XiaoQi'
这样修改的话,那么这个就不是在原型链上修改了,而是在a自己的身上增加了一个LastName='XiaoQi'
属性,原型链上的LastName
还是Qi
。
注:原型上想要修改自己的属性,除非
Person.prototype.LastName = 'XiaoQi'
这么来修改,要是想通过对象来改原型的东西,那么基本是不可能的。
- 增
和改的原理类似,除非是调用Person.prototype.xxx来增加属性,比如:
Person.prototype.age = 18 //那么我们再来看看原型上的属性 console.log(Person.Prototype); // {LastName: "Qi", age: 18, constructor: ?}
- 删
delete Person.xxx
这样的话是可以删除原型上的属性的,但要是delete a.age
的话,虽然返回的是一个true,但是你在访问a.age
的话,还是会返回18
。虽然他返回的是true
,那是以为你想删除一个你没有的属性,那么电脑当然是同意的啦,所以返回的是true
。
- prototype (原型对象)
定义:
prototype
属性是函数独有的,它的含义是函数的原型对象,也就是这个函数所创建的实例的原型对象;这个属性是一个指针,指向一个对象。
用处:包含所有实例共享的属性和方法
- constructor (构造器)
定义:在这个原型的内部,系统自带了一个属性叫做
constructor
。
function Car () {} var a = new Car(); console.log(a.constructor)//function Car() {}
在上面这个构造函数中,a
的constructor
就是Car
。
作用:让构造函数构造出的所有对象,想找它的构造器可以找到。就好比,你的爸爸给你写了一个你家的家庭地址,当你出去玩想回家的时候,可以通过你爸爸写给你的地址回到家。这个
==constructor是可以被人为的修改的==constructor
就可以看作是你爸爸给你写的家庭地址。
如:
function A() {} function B() {} A.prototype = { constructor: B } var a = new A() console.log(a.constructor); //B() {}
- __proto__ 我们先来看看这个例子
Person.prototype.name = 'abc'; function Person () {} var a = new Person(); console.log(a.__proto__); //打开这个对象▼Object //name: "abc" //?constructor: ? Person() //?__proto__: Object
从上面这个例子就可以看出来,这个__proto__
里面放的就是原型。
那么这个__proto__
到底是哪里来的呢?或者说这个__proto__
放在原型里面有什么用呢?在这里,我们就要给构造函数的内部原理填上一个坑了。在构造函数内部原理
那么这个new
之后的第一步,是会在里面隐式的创建一个var this = {}
的一个类似空对象的对象,其实这个对象它不是空的,里面本来就有这个__proto__
属性,指向的是Person.prototype
。
__proto__
到底有什么用呢?【构造函数、原型、原型链】当你访问这个对象的属性的时候,这个对象如果没有这个属性的话,它就会通过
为什么自己身上没有会去找原型呢? 因为__proto__
指向的索引,去找这个哥们身上有没有你想要的那个属性,它相当于一个链接的关系,把原型和自己连接在一起,假如你现在访问a.name
,他会现在自己的身上找有没有name
这个属性,如果没有的话,他就会沿着__proto__
指向的地方去找它身上有没有这个属性。
__proto__
里面存放的就是这个对象的原型。
- 例一
Person.prototype.name = 'sunny'; function Perosn () {}; var a = new Person(); Person.prototype.name = 'cherry'; console.log(a.name); //cherry
解释:现在访问的这个
name
属性,自己的身上没有,就沿着__proto__
的指向找。它的__proto__
指向的是Person.prototype
,现在又把Person.prototype
的值给修改了,那么访问的肯定是修改后的值啦。
- 例二
Person.prototype.name = 'sunny'; function Person() {}; var a = new Person(); Person.prototype = { name : 'cherry', }; console.log(a.name); // sunny
为什么输出的是sunny
呢?
首先我们来搞清楚Person.prototype.xxx
和Person.prototype = {}
有什么不一样!
-
Person.prototype.xxx
是在原来的基础上把属性给修改了。 -
Person.prototype = {}
是把原型给修改了,换了一个新的对象。
解释:上面的是在
new
之后,发生了一个过程:var this= {__proto__: Person.prototype}
, 然后a的__proto__
:Person.prototype
,然后__proto__
:Person.prototype
和Person.prototype
是一个人,也就是Person
指向的空间,当a构建完成之后,Person.prototype
把自己的空间转移了,但是__proto__
所指向的没有改变,还是原来的,原来空间的name
是sunny
,所以当访问a.name
的时候,返回的还是sunny
。
-
- 例三
Person.prototype.name = 'sunny'; function Person () {} Person.prototype = {name : 'cherry'}; var a = new Person(); console.log(a.name); //cherry
为什么会这样呢?
解释:函数的提升,让后面的
Person.prototype = {name: 'cherry'}
把前面的Person.prototype.name = 'sunny'
给覆盖了。因为要new
之后才会有隐式的三步,才会在里面创建一个this的类似空对象的属性,里面的__proto__
指向的是覆盖后的房间,所以访问a.name
的返回结果是cherry
。而上面的那个是先new
了之后把那个对象给生成后,才修改的,那个时候已经玩了;现在这个是先修改,在创建的对象,所以是cherry
。
定义: 在原型上面加一个原型再加一个原型,这样的一个方法,把原型形成链,访问顺序也是按照链的顺序,像作用域链一样的去访问东西,叫做原型链。原型链的连接点就是当了解了构造函数和原型之后,我们理解原型链的话就更加的简单了。我们先看看下面这个代码__proto__
,访问的顺序都是先近后远的查。
// 首先我们创建一个构造函数
function Foo () {}//这个Foo构造函数的原型是Foo.prototype//这个Foo构造函数的__proto__指向的是Function.prototype
console.log(Foo.__proto__ === Function.prototype);
// trueconsole.log(Function.constructor);
//Function () {} (函数对象)// 这个时候创建一个实例化对象foo
var foo = new Foo();
console.log(foo.__proto__);
//Foo.prototypeconsole.log(Foo.prototype.__proto__);
//Object.prototypeconsole.log(Object.prototype.__proto__);
//unllconsole.log(Object.prototype.constructor);
//Object (){} (函数对象)
下面我们就用图来好好的捋一下思路吧
文章图片
原型链 我们在做几道题目来巩固一下原型链吧
- 例一
Person.prototype = {
name: 'a',
sayName: function () {
console.log(this.name);
}
};
function Person () {
this.name = "b";
};
var c = new Person();
console.log(c.name);
// b
console.log(Person.prototype.sayName);
// a
- 例二
Person.prototype = {
height: 100
};
function Person () {
this.eat = function () {
this.height ++;
}
}
var a = new Person();
a.eat();
// 101
今天就先到这里吧,后面我会继续补充的,第一次写文章,请大佬轻喷~
推荐阅读
- 一个人的碎碎念
- 野营记-第五章|野营记-第五章 讨伐梦魇兽
- Shell-Bash变量与运算符
- 清明,是追思、是传承、是感恩。
- 牛人进化+|牛人进化+ 按自己的意愿过一生
- 七老修复好敏感、角质层薄、红血丝
- 华为旁!大社区、地铁新盘,佳兆业城市广场五期!
- 标签、语法规范、内联框架、超链接、CSS的编写位置、CSS语法、开发工具、块和内联、常用选择器、后代元素选择器、伪类、伪元素。
- 螃蟹和这些食物同吃,轻则腹泻、重则中毒!要小心哦~
- 八、「料理风云」