好好聊聊原型

最近一直被人问到这个问题,这东西不就是完成你对象的继承吗?

认识论:认识论是探讨人类认识的本质、结构,认识与客观实在的关系,认识的前提和基础,认识发生、发展的过程及其规律,认识的真理标准等问题的哲学学说。——世界观

方法论:方法论,就是人们认识世界、改造世界的根本方法。——工具

主体:哲学上指对客体有认识和实践能力的人,是客体的存在意义的决定者。“我” 客体:客体(object )可感知或可想象到的任何事物。“我”之外的一切事物,是认识与实践的对象 本体:本体指事物的本身;引申为根本的 实体:马克思主义以前的哲学,认为变化着的事物有一种永恒不变的基础,就是实体。马克思主义哲学认为所谓实体就是永远运动着和发展着的物质。实体是客观的物质世界,指有形、有象之物。

类型 类型的实例 对象和实例 类型对象的实例 对象引用和对象 对象类型

准备知识

  1. 线程栈 一个Windows进程可能有多个线程,线程创建时会分配到1MB的栈,栈空间用于向方法传递实参,方法内部定义的局部变量也在栈上面。

  2. JS的单线程

  • DOM节点作为临界资源并没有多线程的需求,而且多线程本身也会带来DOM操作的复杂性;
  • 单线程可避免上下文的切换,提高效率;
  1. 值类型和引用类型
  • 值类型:栈空间中分配空间,变量中直接存储值;
  • 引用类型:堆内存中分配对象的存储空间,栈空间中分配变量的空间,其中的值为指向对象的引用;
  1. 方法调用与执行 此处我们参考CLR的执行方式:
  • "序幕"代码(prologue code):方法开始工作时对其进行初始化。
  • "尾声"代码(epilogue):方法完成时对其进行清理,以便返回调用者。
  • 帧栈(stack frame):代表当前线程的调用栈中的一个方法调用。执行线程过程中,进行的每个方法调用时都会在调用栈中创建并压入一个帧栈。

涉及到的操作:

  • wind:调用方法时压住帧栈
  • unwind:方法执行完毕时弹出帧栈

执行环境(执行上下文)

控制器转入ECMA脚步的可执行代码时,控制器会进入一个“执行环境”(execution context)。当前活动的多个执行环境在逻辑上形成一个栈结构。 该逻辑栈再最顶层的执行环境成为当前运行的执行环境(running execution context,活动执行环境)。任何时候,当控制器从当前执行环境相关的可执行代码转入与该执行环境无关的可执行代码时,会创建一个新的执行环境。新建的这个执行环境会被压入栈中,成为当前运行的执行环境。

组件作用目的
词法环境指定一个词法环境对象,用于解析该执行环境的代码创建的标识符引用
变量环境指定一个词法环境对象,其环境数据用于保存该执行环境的代码通过变量表达式和函数表达式创建的绑定
>This绑定指定该执行环境内的ECMA脚步代码中this关键字所关联的值

要点:

  1. 词法环境是一个用于特定变量和函数标识符在ECMAScript代码的词法嵌套结构上关联关系的规范类型。由一个环境记录项和可能为空的外部词法环境的引用构成。通常词法环境会与特定的 ECMAScript 代码诸如 FunctionDeclaration,WithStatement 或者 TryStatement 的 Catch 块这样的语法结构相联系,且类似代码每次执行都会有一个新的语法环境被创建出来。
  • 环境记录项记录了在它的关联词法环境域内创建的标识符绑定情形
  • 内部词法环境的外部引用是逻辑上包含内部词法环境的词法环境。 (这句很关键) 外部词法环境自然也可能有多个内部词法环境。例如,如果一个 FunctionDeclaration 包含两个嵌套的 FunctionDeclaration,那么每个内嵌函数的词法环境都是外部函数本次执行所产生的词法环境。
  1. 词法环境和变量环境组件始终为词法环境对象。当创建一个执行环境时,其词法环境变量环境组件最初是同一个值。在该执行环境相关联的代码的执行过程中,变量环境组件永远不变,而词法环境组件有可能改变。
  2. 执行环境是一个纯粹的标准机制,在ECMAScript程序中是不可能访问到执行环境的。

注1:WithStatement 或者 TryStatement 的 Catch 块 这两个语句都会在作用域链的前端添加一个执行环境的变量对象。对于with语句来说,其变量对象中包含着指定为指定对象的所有属性和方法所作的变量声明。对于catch语句来说,其变量对象中包含的是被抛出的错误对象的声明。这些变量对象都是只读的,因此在with和catch语句中声明的变量都会被添加到所在的执行环境的变量中。

var myColor = {};
var color = 'blue';
function changeColor(){
    var anotherColor = 'red';
    function swapColors(){
        var tempColor = anotherColor;
        anotherColor = color;
        color = tempColor;
        myColor.color = color;
    }
    //这里能访问
    swapColors();
}
changeColor();
with(myColor)
{
    color = 'black';
    var color1 = 'gray';
}
alert('Color is now ' + color);
alert('MyColor is now ' + myColor.color);
console.log(myColor);
//console.log(color1); //去掉注释试试看是什么
//console.log(this);
复制代码

注2:

声明式环境记录项:每个声明式环境记录项都与一个包含变量和(或)函数声明的 ECMAScript的程序作用域相关联。声明式环境记录项用于绑定作用域内定义的一系列标识符。

对象式环境记录项:每一个对象式环境记录项都有一个关联的对象,这个对象被称作绑定对象 。对象式环境记录项直接将一系列标识符与其绑定对象的属性名称建立一一对应关系。

正是这些执行环境栈的执行环境的词法环境与其包含的外部词法环境的引用构成了一个链表的结构——作用域链

查询标示符的过程是从作用域链的前端开始,向上逐级查询与给定名字匹配的标示符. 如果在局部环境中找到了该标示符,搜索过程停止,变量就绪,否则搜索过程将一直追溯到全局环境的变量对象

// 声明式记录项:记录标识符a的声明的作用域关联
// 对象式记录项:绑定对象(X),标识符b、c与绑定对象(X)的属性的对应关系
var a = {
    b:'',
    c:''
};
复制代码
// 外部代码,创建外部执行环境,并创建其变量对象A
var color = 'blue';
 
function changeColor(){
    if(color === 'blue'){
        color = 'red';
    } else {
        color = 'blue';
    }
}
changeColor();  // 执行函数changeColor, 进入该函数执行时创建新的执行环境,创建变量对象B,变量对象
alert('Color is now '+ color);
复制代码

简而言之:每个函数在被调用时都会创建自己的执行环境。当执行流进入一个函数时,函数的环境就会被推入一个环境栈中。而在函数执行之后,栈将其环境弹出,把控制权返回给之前的执行环境。

this

当控制进入执行环境时,创建并初始化作用域链,进行变量初始化,并决定 this 值。作用域链的初始化,变量的初始化和 this 值的决定取决于进入的代码类型。

  1. 全局代码 global

this 值为全局对象

  1. 求值代码 eval

当控制进入求值代码的执行上下文时,把前一个活动的执行环境引用为调用环境,用它决定作用域链、可变对象和 this 值。若调用上下文不存在,就把它当作全局对象,进行作用域链和变量的初始化及 this 值的决定。

  1. 函数代码 function

this 值由调用者提供,若调用者提供的 this 值不是一个对象(注意,null 不是对象),则 this 值为全局对象

Function对象

  1. 当将 Function 作为函数来调用,而不是作为构造器,它会创建并初始化一个新函数对象。所以函数调用 Function(…) 与用相同参数的 new Function(…) 表达式创建的对象相同。

  2. 当 Function 作为 new 表达式的一部分被调用时,它是一个构造器:它初始化新创建的对象。

原型(Function.prototype)

为其他对象提供共享属性的对象。( prototype 属性的值用于初始化一个新创建对象的的 [[Prototype]] 内部属性)

当构造器创建一个对象,为了解决对象的属性引用,该对象会隐式引用构造器的“prototype”属性。通过程序表达式 constructor.prototype 可以引用到构造器的“prototype”属性,并且添加到对象原型里的属性,会通过继承与所有共享此原型的对象共享。另外,可使用 Object.create 内置函数,通过明确指定原型来创建一个新对象。

var a = Object.create({name:'123'})
复制代码
  1. Function 构造器自身是个函数对象,它的 [[Class]] 是 "Function"。Function 构造器的 [[Prototype]] 内部属性值是标准内置 Function 的 prototype 对象。
Function.__proto__ == Function.prototype // true
复制代码
  1. Function 的 prototype 对象自身是一个函数对象 ( 它的 [[Class]] 是 "Function"),调用这个函数对象时,接受任何参数并返回 undefined。
typeof(Function.prototype) === 'function' // true
Function.prototype('a','b') === undefined // true
复制代码
  1. Function 的 prototype 对象的 [[Prototype]] 内部属性值是标准内置 Object 的 prototype 对象
Function.prototype.__proto__ == Object.__proto__ // false
Function.prototype.__proto__ == Object.prototype // true
Object.__proto__ === Object.prototype // false
Function.prototype == Object.__proto__ // true
Object.__proto__.__proto__ == Object.prototype // true
复制代码
  • Function.prototype.constructor
  • Function.prototype.toString()
  • Function.prototype.apply (thisArg, argArray)
  • Function.prototype.call (thisArg [ , arg1 [ , arg2, … ] ] )

原型链

每个由构造器创建的对象,都有一个隐式引用 ( 叫做对象的原型 ) 链接到构造器的“prototype”属性值。再者,原型可能有一个非空 (non-null) 隐式引用链接到它自己的原型,以此类推,这叫做 原型链 。当向对象的一个属性提出引用,引用会指向原型链中包含此属性名的第一个对象的此属性。换句话说,首先检查直接提及的对象的同名属性,如果对象包含同名的属性,引用即指向此属性,如果该对象不包含同名的属性,则下一步检查对象的原型;以此类推。

CF 是一个构造器(也是一个对象)。五个对象已用 new 表达式创建 : cf1, cf2, cf3, cf4, cf5。每个对象都有名为 q1 和 q2 的属性。虚线表示隐式原型关系;例如:cf3 的原型是 CFp。构造器 CF 自己有名为 P1 和 P2 的两个属性 , 这对 CFp, cf1, cf2, cf3, cf4, cf5 是不可见的。CFp 的名为 CFP1 的属性共享给 cf1, cf2, cf3, cf4, cf5 ( 没有 CF), 以及在 CFp 的隐式原型链中找不到任何名为 q1, q2, 或 CFP1 的属性。 请注意 ,CF 和 CFp 之间没有隐式原型链接。

不同于基于类的对象语言,属性可以通过赋值的方式动态添加给对象。也就是说,构造器并不是非要对构造的对象的全部或任何属性命名或赋值。上图中,可以给 CFp 添加新属性值的方式为 cf1, cf2, cf3, cf4,cf5 添加一个新的共享属性。

Function.prototype.P1 = 'p1';
Function.prototype.P2 = 'p2';
var CF = function(){
}

var CFp = {CFP1:'cfp1'}

CF.prototype = CFp;

var cf1 = new CF();
复制代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值