今天又在打代码

导航

this的指向及原型和原型链 【作为构造函数被new调用】

this在函数中的指向场景有4种:

1. 作为构造函数被new调用

 1 <!DOCTYPE html>
 2 <html>
 3     <head>
 4         <meta charset="utf-8">
 5         <title></title>
 6     </head>
 7     <body>
 8         <script>
 9             console.log(this)
10             function Person () {
11                 console.log(this)
12             }
13             var person = new Person();
14         </script>
15     </body>
16 </html>

在代码里面首先是先输出此时的this指向,然后再实例化一个对象并在构造函数里面输出this的指向,此时输出结果是:

 

 

 一开始默认我们的this指向的是window,但是当我们用new调用构造函数后,this的指向就变成了这个新对象,并且里面还有个默认的属性__proto__,实际上当我们在使用new调用构造函数时,会执行以下的操作:

1.创建一个新对象

2.构造函数的propotype指向当前我们这个新对象的__proto__

  首先,需要先来了解一下什么是原型,原型(__proto__)是一个对象,其它的对象可以通过原型来实现属性的继承,并且大多数的对象的原型都是__proto__

  例子:我们分别定义一个数组,一个对象和一个函数

1     var arr = ['1','2']
2     console.log(arr)
3     var obj = { name: '小红'}
4     console.log(obj)
5     function fn () {}
6     console.log(fn)

  输出结果:

  

 

 

   可以看到输出的arr和obj都有一个__proto__(原型对象),而我们的定义的方法并没有原型对象。我们再将arr里面的原型对象展开:

  

   可以看到这个对象里面的方法都是我们数组所包含的方法,但是我们的arr可以继承到__proto__(原型对象)里面的属性和方法,也就是我们经常这样来访问:arr.length(), 而funtion的原型是prototype,这是函数才有的属性,我们每个对象有的属性是__proto__,__proto__不是一个标准的名字,它的原名是 [ [ prototype ] ],只是脚本中并没有访问 [ [ prototype ] ]的方法,而在部分浏览器中才能支持这个属性。

  在__proto__里面有个contructor构造器(是个函数),我们的arr是什么样子全靠这个构造器,这个函数里面有prototype,而它里面的所有又和我们proto里面是一样的。看下面两个截图:

左边的是arr的原型,右边是arr原型里面的展开,可以看到我们构造器里面的prototype里面的东西和左边proto展开的东西是一模一样的!

这可以说明一个点就是,大多情况下,__proto__可以理解为其构造器(是个函数)的原型(函数的原型prototype), 也就是说可以这样来看__proto__ === construter.prototype

 

 

 

 

   再来看下原型的指向,它取决于对象创建时的实现方式:

  ① 字面量创建对象,也就是我们平时经常用的创建方式,var xxx = { ...... } , 此时它的原型就是__proto__,它等于其构造器的原型。

  

 

 

 

  ② 构造器创建对象(构造函数),var X = function () {}  var x = new X() 这就是一个new出来的对象,此时它的指向会不一样,我们X的原型还是prototype(因为构造函数也是函数,只是多了一个new),而我们x的原型,虽然输出的结果多了个A,但是它的原型依旧是其构造器的原型,即x.__proto__ === X.prototype  也等于 x.__proto__ === x.constructor.prototype 只是x的构造器现在是我们自定义的X,但是并没有什么大变化

 

 

 

 

   ③通过Object.create 方式来创建对象(有点有趣的变化)

 

 

   这儿展开后我们的b的__proto__的指向发生了变化,不再是其构造器的原型了,

 

 

   但是变成了这个,也就是我们创建其的对象a

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

  原型基本就讲到这边,然后是原型链的讲解,先看下下面的代码和输出:

1 var A = function() {}
2 var a = new A()
3 console.log(a.__proto__)
4 console.log(a.__proto__.__proto__)
5 console.log(a.__proto__.__proto__.__proto__)

  好吧第一个应该都知道a的原型是等于其构造器的原型的 a.__proto__ === a.constructor.prototype === A.prototype,

  而第二项也就是求A.prototype的原型,由于我们也可以将其看做一个对象,那么这个对象的原型依旧是其构造器的原型,它的构造器是Object() {....省略里面的属性和方法..} 那么控制台来输出看下是不是,以下两句是相等的,都会返回true,你们可以自己试一下

 

 

 

   而第三项就是输出Object.prototype的原型了,但是Object已经是最底层的了,所以它没有原型,会输出null

  这样就是我们所说的原型链了,一个对象的原型等于其构造器的原型,而其构造器的原型又等于其构造器的原型的原型,一直到最后为null,在js中万物都是对象,所以会新成一条由__proto__构成的链条,递归访问__proto__必须有个出口,且数值为null,当js引擎查找对象的属性的时候,是先查找对象本身是否存在该属性,如果不存在,则沿着原型链不断地进行查找,有就使用没有就为空,它不会查找自身的prototye,下面是个原型链查找的例子:

  

1 var arr = ['1','2','3']
2 console.log(arr.unshift())

这边我们调用了unshift方法,其实是用的原型链上的Array对象里面的unshift方法,我们来是试下随便定义一个方法比如说,arr.showArray(),

此时控制台会报错

 

 这是因为整条原型链上都找不到这个方法来调用,但是我们可以为它的原型链上加上这个方法,再来调用:

 

 此刻再调用的话就能够访问到了

 

3.将新对象赋值给当前的this

4.执行构造函数(为这个新对象添加属性,继承其构造函数的原型上的所有属性和方法)

5.如果构造函数没有其它的返回对象,则默认返回这个新对象,否则则会被忽略

posted on 2020-06-12 01:27  今天又在打代码  阅读(333)  评论(0编辑  收藏  举报