首先,先用构造函数创建一个对象
function Person() {
}
const person = new Person();
person.name = 'yxh';
console.log(person,name); // yxh
以上Person就是一个构造函数,我们使用new创建了一个实例对象person。
首先,每个函数都有一个prototype属性,并且是只有函数才会有的属性, 然后这个函数的prototype属性指向了一个对象,这个对象就是使用该构造函数创建的实例的原型
function Person() {
}
Person.prototype.name = 'yxh';
const person1 = new Person();
console.log(person1.name); // yxh
每一个js对象,除了null,在创建的时候就会与之关联另外一个对象,这个对象就是原型,也就是上面例子的person的原型
进一步的解释:每个构造函数都会有一个prototype的属性,这个属性指向由这个构造函数创建的实例的原型
答案是使用__proto__,每个js对象,除去null都具有这个属性,这个属性就指向该对象的原型,可以理解为这个对象 其实是内存中的一个地址,而不管是实例的__proto__,即person1.__proto__或是构造函数的prototype属性,即Person.prototype 都指向了这个地址,这样也就相当于是通过实例的__proto__属性,将实例将其原型联系起来了
即Person.prototype === person1.proto
构造函数通过prototype指向了原型,而原型通过constructor属性指向回构造函数 即:Person.prototype.constructor === Person
一句话简而言之:实例中没有的属性或是方法会到他的原型中去找,如果还是找不到,就去找原型的 原型,一直找到最顶层为止(这一条由原型串联起来的链条也就叫做原型链了)
原型的原型是什么?或是原型的原型的原型是什么?或是原型链的最顶端是什么?像是绕口令一样啊,(((φ(◎ロ◎;)φ))) 既然原型是一个对象,那么我们可以通过new Object()的方式来创建它,这样一来,我们可以很容易的知道 原型的原型其实是Object.prototype,也就是 Person.prototype.proto === Object.prototype, Object.prototype 这个原型也有自己 的constructor属性,这个属性指向Object构造函数
实例的原型的原型是Object.proptype,那么Object.prototype.__proto__其实是null,也就是 Object.prototype.proto === null;null表示没有对象,此处不应该有值,也就是说原型的原型是没有原型的,到 Object.prototype就是原型链的顶端,查找属性或者方法也就是查找到Object.prototype属性
即:person1 => Person.prototype => Object.prototype => null 而将各个原型链接起来产生关联的正是这个__proto__, 即: 1.person1.proto === Person.prototype, 2.Person.prototype .proto === Object.Prototype. 3.Object.Prototype.proto === null
person1.constructor === Person person1中并没有constructor方法,所以去它的原型中去找,而原型中恰好有这个方法,所以以上成立 即Person.prototype.constructor === Person
以上并不是Function自己生成了自己,而是Function本身是一个内置对象,在运行前就已经存在了,所以并不会自己去生成自己,它一直都在,之所以这样写是为了和其他对象的原型链保持一致而已,可能是 js所特有的吧
域,顾名思义就是区域,作用域就是代码中定义变量的区域 其中作用域规定了如何查找变量,也就是确认当前执行的代码对变量的访问权限,也就是说代码仅能够访问到作用域内的变量 js使用的是静态作用域也就是词法作用域,与静态作用域向对应的是其他语言的动态作用域
静态作用域,函数的作用域在函数被定义的时候就确定了 动态作用域,函数的作用域是在函数被调用的时候才决定的
理解了这句话,闭包也就理解了
js引擎并非是一行一行的去分析并执行程序,而是一段一段的分析执行的,当执行一段代码的时候,会进行一个准备工作 当执行一段代码时会进行一个准备工作,比如变量提升和函数提升
其实是根据可执行代码来进行划分的,共有三种,全局代码,函数代码和eval代码 而这个准备工作其实就是执行函数的时候去创建执行上下文(execution context)
那么当我们写的函数很多的时候,该如何管理创建的那么多执行上下文呢?答案就是使用执行上下文栈来管理执行上下文
js引擎在执行代码的时候,首先遇到的就是全局代码,然后创建全局执行上下文,所以一开始先会在执行上下文栈中压入一个全局执行上下文 只有当整个全局代码都执行结束的时候,执行上下文栈才会被清空,所以在全局代码结束之前,这个全局执行上下文 就一直都在执行上下文栈中 然后当js引擎遇到第二种可执行代码,函数代码的时候,就会创建该函数代码的执行上下文,然后将其压入执行上下文栈中 当这个函数执行完毕后,js引擎就会将该函数的执行上下文弹出执行上下文栈
js引擎在执行函数时,都会创建对应的执行上下文,同时把这个执行上下文压入执行上下文栈进行管理,当函数执行完成后,再将该 执行上下文弹出执行上下文栈, 每个执行上下文,都包含三个很重要的属性 1.变量对象(Variable, 也就是VO) 2.作用域链 3.this
变量对象中保存了执行上下文中定义的变量和函数声明 而因为执行上下文分为全局执行上下文和函数执行上下文,所以变量对象也分为两种 一是全局执行上下文的变量对象,另一个是函数执行上下文的变量对象 我理解的是全局执行上下文的变量对象就是全局对象 果然猜对了。。哈哈
全局对象可以通过this引用,或者this就指向这个全局对象,在浏览器中全局对象就是window 全局对象是Object构造函数的一个实例 全局对象中有一堆预定义的函数和属性,这个打开浏览器的控制台,输入window就可以看到 全局对象中保存了各种全局变量,可以说全局对象是全局变量的宿主
既然全局执行上下文的变量对象就是全局变量,那么函数执行上下文的变量对象就是,, 其实是叫做活动对象也就是(activation object,也就是AO)
js引擎在执行函数代码的时候,会创建函数的执行上下文,此时的执行上下文中的变量对象(VO)中的 属性都是不可以访问的,这个阶段属于未执行阶段,当js引擎将该函数执行上下文压入执行上下文栈执行的时候 此时就进入了执行阶段,而此时的变量对象(VO)就变成了活动对象(AO),里面的属性就都能被访问了,其实他们都属于同一个 对象,只是在不同的执行阶段,名字不一样,可访问的内容也不一样
未执行阶段:变量对象初始化,只初始化了Arguments对象,这也就是此刻的变量对象中的属性不可以被访问的原因,因为没有可以访问的属性啊。。 执行阶段分为两个阶段: 1.进入执行上下文:变量对象转变为活动对象,活动对象的各个属性被创建,即各种变量被创建, 包括变量名和对应的值,但是没有实参的变量属性值设置为undefined;活动对象的函数被声明 如果存在相同属性则替换覆盖这个属性 2.执行阶段,就是会顺序执行代码,然后根据代码去修改活动对象的各种属性的值