学习JavaScript原型笔记

227 阅读5分钟

学习JavaScript来深入理解其工作机制

原型与闭包是JavaScript与其他面向对象语言差异最大的两个点,深入学习js需要我们理解原型

什么是对象❓

在js中我们经常使用typeof运算符来判断一个变量的类型。 常见的类型可以分为值类型与引用类型


值类型有:undefined、number、string、boolean

引用类型有:obejct、function(数组、对象、空值都是object)

对象是什么??对象就是一些属性值的集合记住这一句话就可以很好地帮助你判断这是不是一个对象

函数与对象的关系

我们都知道,对于引用类型的判断,我们常常使用instanceof运算符。我们队一个函数使用这个判断符时,得出的结果是object。那么函数与对象之间究竟有着什么样的区别与联系呢?

在es6之前,如果我们想要定义一个对象,那么我们常用的创建构造函数的方式就是。

function Fn() {
            this.name = '王福朋';
            this.year = 1988;
        }
        var fn1 = new Fn();

很显然,我们创造了一个fn1对象,有名字和出生年份两个属性。

这里有一个重要的结论就是*一切的对象都是通过函数来创造的(构造函数)*不信的话我们可以对Object和Array做typeof运算,得到的结果就是function。

本小节最后,我们来理一理思路,所有的对象都是由函数构造的,而一个函数又是一个对象。那么问题来了。。。hahahah

函数的prototype

![函数与原型的关系](http://i2.bvimg.com/668731/279cd18f4be682e0.png)

如图所示,每一个函数都有它的原型(prototype),原型是是一个对象,里面有着许许多多的属性值。我们在使用函数创建对象时,函数中的prototype就会创建一个引用,这个引用就是__proto__这是每一个对象的隐藏属性,指向构造函数的原型。即fn.proto===Fn.prototype

隐式原型

隐式原型指的就时对象的隐藏属性_proto__

自定义函数的_proto_

每个函数function都有一个prototype,即原型。这里再加一句话——每个对象都有一个__proto__,可成为隐式原型。

我们都知道函数instanceof Obejct的值为true。所以它的本质就是Object的prototype。那么Obeject的_proto_指向哪里呢?答案是null。这是一个特例。

那么函数是一个对象,对象就有隐式原型,它的隐式原型指向的就是创造这个对象的函数Function的prototype。所以我们可以得到一个很绕的原型链图。

![原型链图](http://i1.bvimg.com/668731/76948b944cede7ac.png)

最后,我们都知道prototype属性是一个对象,那么Function。prototype这个对象的_proto_指向哪里呢?当然只指向Object.prototype啦!!

继承

首先我们需要知道,所谓继承,继承的是什么。

function Foo () {}

var f1 = new Foo()

f1.a = 100

Foo.prototype.a = 1
Foo.prototype.b = 100

console.log(f1.a) // 100
console.log(f1.b) //100

通过之前的学习,我门已经知道了f1具有__proto__属性,这个属性指向构造对象的函数Foo的prototype上。那么当我们访问f1的属性值时,js会先从对象的基本属性中查找,如果没有就从__proto__这条链一直往上找。这种继承的属性寻找机制,就是原型链。

我们可以使用hasOwnProperty()方法来查询某个属性值是否属于这个对象的基本属性,如果是从原型链上继承过来的就会返回false。

执行上下文

先看三个例子

// Example 1
console.log(a) //Eroor
//-----------------------------
console.log(a) //undefined
var a
//-----------------------------
console.log(a) //undefined
var a = 10
//-----------------------------
// Example 2
console.log(this) //任何情况下都有值,但是取值会变
//-----------------------------
// Example3
console.log(f1) // function f1 () {}
function f1 () {}
console.log(f2) // undefined
var f2 = function () {}

我们来看上面三个例子。

第一个例子的一二个,我想大家都知道为什么,一个是没有声明,第二个是存在声明提升,但是没有赋值,第三个的原因是,console的时候还没有执行赋值。

第二个例子,后面会详细说。

第三个例子,首先我们需要知道第一个是函数声明,第二个是函数表达式,函数声明同样的具有提升,所以有输出,而表达式理由同例1的第三个。

通过上面的例子我们发下,在不同的语句条件下,访问某些变量,这些变量的值是会根据语句条件的不同而不同的。我们就管这种语句条件叫做上下文环境。

执行上下文环境下一个通俗的定义——在执行代码之前,把将要用到的所有的变量都事先拿出来,有的直接赋值了,有的先用undefined占个空。

this

在函数中this到底取何值,是在函数真正被调用执行的时候确定的,函数定义的时候确定不了。因为this的取值是执行上下文环境的一部分,每次调用函数,都会产生一个新的执行上下文环境。

情况1:构造函数

不仅仅是构造函数的prototype,即便是在整个原型链中,this代表的也都是当前对象的值

function Foo () {
  this.a = 1
  console.log(this)
}
var f1 = new Foo() // Foo {a:1}
Foo() //Window

当函数作为构造函数使用时,this指向的就是它即将new出来的对象,即f1,但是如果直接使用指向的就是Window

方法

一个对象调用它属性中的某个方法时,方法中的this指向的就是这个对象

call && apply

当一个函数被call或者apply调用时,函数中this的值就是传入的对象

函数

在全局环境与普通函数调用时,this的值都是Window。

var obj = {
  fn: function () {
    function f () {
      console.log(this) //Window
    }
  }
}

上面这是一种特殊情况,我们都知道,fn作为obj的方法,它的this应该是obj。但是fn中的f是一个普通函数,普通函数的this就是Window。