javascript学习笔记(18)--new&构造函数

这节开始之前,我们先详细认识一下new

new

其实我们很早就跟new打过交道

var a =new Date()
var a=new Date//两个都可以

a
Date Wed Jun 24 2020 07:59:12 GMT+0800 (中国标准时间)
typeof a
"object"

typeof new Date
"object"
typeof new Date()
"object"

new最基本的使用方法就是new funcion(),new后面跟一个函数
然后会返回一个对象(准确的来说是实例)
new的返回结果都是object
javascript学习笔记(18)--new&构造函数_第1张图片
通过上面这个也可以看到,通过new可以创建一个原型是我们函数原型的实例

在上节我们提到了函数内部4种变量的访问,分为私有变量,静态变量,实例变量和prototype
而通过实例化就可以访问内部的实例变量和prototype
如果还有不清楚的就可以再看一看上一节的内容
javascript学习笔记(17)–prototype
所以new没什么复杂的,就五个特性
1.实例化(对象)
2.原型继承
3.实例变量和原型变量的访问
4;将新创建的对象作为this的上下文 ;
5. 如果该函数没有返回对象,则返回this

最重要的都是我们构造函数的写法

构造函数

除了直接用{ … }创建一个对象外,JavaScript还可以用一种构造函数的方法来创建对象,它的用法是,先定义一个构造函数

 function Student(name) { 
 this.name = name;
  this.hello = function () { alert('Hello, ' + this.name + '!'); } }

这上面定义的其实就是实例变量

这看起来好像是一个普通函数,但是在JavaScript中,可以用关键字new来调用这个函数,并返回一个对象

 var xiaoming = new Student('小明');
  xiaoming.name; // '小明' 
  xiaoming.hello(); // Hello, 小明!

注意,如果不写new,这就是一个普通函数,它返回undefined,但是,如果写了new,它就变成了一个构造函数,它绑定的this指向新创建的对象,并默认返回this,也就是说,不需要在最后写return this;

新创建的xiaoming的原型链是
xiaoming ----> Student.prototype ----> Object.prototype ----> null
也就是说,xiaoming的原型指向函数Student的原型
而函数的原型就是我们的Object.prototype

如果你又创建了xiaohong、xiaojun,那么这些对象的原型与xiaoming是一样的
xiaoming (xiaohong) -→ Student.prototype ----> Object.prototype ----> null

用new Student()创建的对象还从原型上获得了一个constructor属性,它指向函数Student本身
javascript学习笔记(18)--new&构造函数_第2张图片我们可以点开xiaoming里面灰色的prototype,然后可以看到下面有一个constructor,而这个构造方法就是我们的函数
constructor(构造函数方法)是用于创建和初始化在类中创建的对象的特殊方法,如果您没有提供自己的构造函数,那么将为您提供一个默认构造函数

下面这几个关系式,看看你可以理解吗
javascript学习笔记(18)--new&构造函数_第3张图片前三个可能很好理解,但是后两个也许就没那么好理解了
注意奥,上节课我们区分了蓝色和灰色的constructor,蓝色的就是我们可以通过变量访问的比如说Student.prototype他就访问的是蓝色的prototype,而灰色的就是上一级的原型,所以需要去上一级访问,一定要记住自己能访问的是自己自带的原型
要区分开继承的原型和自己自带的原型

那么你也许会问,xiaoming的constructor是继承的原型里带的,为什么也可以访问?
不要忘了,我们是怎么访问原型的
假设a是变量,name是原型里的属性
如果是访问自带的原型里的变量,就要用
a.prototype.name
而如果是继承原型里的属性,就要用
a.name
所以4,5也可以理解了吧

再补充一句
灰色的prototype是体现构造我们当前对象的原型
蓝色的prototype是我们实例化后的对象的原型,是生成我们实例的原型

灰色的prototype的constructor是指向我们上一级的构造函数
而蓝色的prototype的construcotr是指向我们自己

xiaoming访问的constructor的属性其实是在访问student的原型里的属性,而这个属性其实就是我们构造函数student本身
(第一次接触可能还是有点绕,把这一节和上一节好好看一下,相信是可以有很大体会的)

当然判断一个实例是不是这个构造函数的实例,还可以用到instanceof


xiaoming instanceof Student; 
true

前面是实例化对象,后面是构造函数

当然我们也可以主动去寻找一个实例的原型,需要用到
getprototypeof()方法
用法Object.getPrototypeOf(实例)
可以通过返回的constructor查看构造函数,进而直到原型
javascript学习笔记(18)--new&构造函数_第4张图片javascript学习笔记(18)--new&构造函数_第5张图片其实getPrototypeOf返回的是构造函数原型

不过还有一个小问题,注意观察
javascript学习笔记(18)--new&构造函数_第6张图片这个其实上一节也讲了一些,不过当时没有联系到构造函数,我们这里构造函数里面定义的都是实例变量和实例函数,因为用的是this,所以函数里面的this其实是绑定window的,所以我们如果不绑定实例就想访问hello和name,是无法通过Student来访问,反而直接通过window来访问
而我们实例化之后,由于每个实例相当于自己保存了一份函数内的实例变量和函数,所以都有自己单独的地址储存,而我们比较函数的时候其实也是比较函数的地址,所以不相等

构造函数里面用实例化变量其实也有好处,就是可以保持我们每个实例之间的独特性,互不干扰,但是对于函数来说就有点别扭了,因为函数还是希望都可以公用,每个实例单独保存一份很占内存,所以我们可以使用prototype来构造函数
javascript学习笔记(18)--new&构造函数_第7张图片这里其实就是用了我们的prototype,上节也讲了,构造函数内部全是prototype实例化的对象实际上就是一个空格,我们真正访问的都是构造函数实例的内容,当于只是占了一个空对象的内存,就像一个指针,我们每个实例其实都是用的一个地址上的内容,所以当然相等啦,而且一旦我们修改了原型上的内容,所有都会变,这就可能会带来不稳定,但是这个其实也可以解决
javascript学习笔记(18)--new&构造函数_第8张图片我们可以通过这样赋值的方式直接在实例下面添加属性,这样就会屏蔽掉原型的变量值
一定要注意哟,xiaoming.name并不是去修改原型上的值,而是创建或者修改自己属性的值,要想修改原型的值只可以…Student.prototype.name="xiaojun"而且还可以同步哈哈

忘记写new

如果一个函数被定义为用于创建对象的构造函数,但是调用时忘记了写new会出现什么样的后果?
在strict模式下,this.name = name将报错,因为this绑定为undefined
javascript学习笔记(18)--new&构造函数_第9张图片
在非strict模式下,this.name = name不报错,因为this绑定为window,于是无意间创建了全局变量name,并且返回undefined,这个结果更糟糕
javascript学习笔记(18)--new&构造函数_第10张图片 就是这个效果啦

所以,调用构造函数千万不要忘记写new
为了区分普通函数和构造函数,按照约定,构造函数首字母应当大写,而普通函数首字母应当小写,这样,一些语法检查工具如jslint将可以帮你检测到漏写的new

函数内部封装new

最后,我们还可以编写一个createStudent()函数,在内部封装所有的new操作
一个常用的编程模式像这样

 function Student(props) {
  this.name = props.name || '匿名';
  this.grade = props.grade || 1;
  Student.prototype.hello = function () { alert('Hello, ' + this.name + '!'); };};
  function createStudent(props) { return new Student(props || {}) }

先定义一个普通的构造函数,在里面定义好实例和原型,然后再用另一个函数封装new构造函数

因为||的运算规则,如果第一个为undefined,那么自然就会是我们预先设定的值,如果传入了值,就会取第一个值,所以这就可以实现一个默认值的效果

注意哟,这里我们传入的是object,传入object有什么优点呢
如果创建的实例有很多属性,我们只需要传递需要的某些属性,剩下的属性可以用默认值。由于参数是一个Object,我们无需记忆参数的顺序,很人性化

这个createStudent()函数有几个巨大的优点
一是不需要new来调用,即使忘了也没事
二是参数非常灵活,可以不传,也可以传一个对象,当然无用的属性也会自动过滤
javascript学习笔记(18)--new&构造函数_第11张图片
后来我又发现了一个点(不知道算不算哈哈)
javascript学习笔记(18)--new&构造函数_第12张图片这个是函数的原型链
Student->Function.prototype->Object.prototype
然后刚才我们实例的对象
xiaoming->Student.prototype->Object.prototype
这个看似可以区分,但是到下一章节就很容易搞混了,因为其实两个乍一看还是蛮像的
javascript学习笔记(18)--new&构造函数_第13张图片
其实道理还是比较简单,我们可以看,Student是function,是个具体的函数,那么他的原型就是Function.prototype啦
像一般情况下,object的上一级就是Object.protytype,不过数组就是个例外啊(我真是希望[1,2,3]的类型是Array这样我的这个也是比较合理了),不过我们可以当作 【1,2,3】就是Array型哈哈
s不过注意啦,typeof Funciton.prototype是个function,这一点很有意思,不过你要相信我们万能的Object.prototype哈哈
javascript学习笔记(18)--new&构造函数_第14张图片
上面是这个函数的具体内容,上面的prototype是蓝色的,下面的是灰色的那个
下面那个链,其实就是具体函数的构造过程,他就相当于是我们的对象,但一个函数作为对象的时候,他就是最下面的那个实例,然后往上就是函数实例的原型,Function.prototype,再往上就是Object.prototype
而当函数有了自身的prototype,这个时候他就不在是一个实例了,他也成为了原型,他的下面就是之后实例化的对象,他的上面又回到了Object.prototype

你可能感兴趣的:(javascript)