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
在这里插入图片描述
通过上面这个也可以看到,通过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本身
在这里插入图片描述我们可以点开xiaoming里面灰色的prototype,然后可以看到下面有一个constructor,而这个构造方法就是我们的函数
constructor(构造函数方法)是用于创建和初始化在类中创建的对象的特殊方法,如果您没有提供自己的构造函数,那么将为您提供一个默认构造函数

下面这几个关系式,看看你可以理解吗
在这里插入图片描述前三个可能很好理解,但是后两个也许就没那么好理解了
注意奥,上节课我们区分了蓝色和灰色的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查看构造函数,进而直到原型
在这里插入图片描述在这里插入图片描述其实getPrototypeOf返回的是构造函数原型

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

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

忘记写new

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

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

  • 5
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值