zoukankan      html  css  js  c++  java
  • JavaScript Oriented[探究面向对象的JavaScript高级语言特性]

    JavaScript Oriented

    探究面向对象的JavaScript高级语言特性

    Prologue . JavaScript Introduce

    1.  JS

    Abstract

      JavaScript是由Netscape公司工程师Brendan Eich研发的脚本语言,经过推广和流行,兼容ECMA-262标准,至今用于描述HTML网页行为。(前端验证,检测,响应,触发,控制等动态行为)

    Knowledge Tree

     

    2.     About Document

      本文涉及到的概念有JavaScript概述,对象类型系统,原型链,作用域链以及上下文this,闭包,命名空间以及面向对象的高级语言特性应用。

      JavaScript知识点庞杂且个人能力和学习时间有限,希望得到有心者更多的鼓励与启发。

    Chapter one . Type Family

    1.  That’s all Object things?

      从面向对象角度理解,JS确实一切皆对象。但是JS是函数式编程,从面向过程角度可以理解为一切皆函数,这可能是JavaScript魅力所在。本文叫做面向JavaScript,偏向从面向对象角度理解。

      如何理解“一切皆对象”。一般来说我们会从对象构造器和原型链解释,本文后文中有详细概述,这里不作初步探讨。

    2.     Begin from GLOBAL

      这里我们从JS最特殊的内置对象GLOBAL开始。

    介绍

    GLOBAL是ECMAScript5规范中两个内置对象其中之一,表示全局对象。

    作用

    任何不属于JavaScript其他对象的属性和方法都属于GLOBAL。

    实现

    JavaScript对其并没有明确的实现。浏览器将GLOBAL作为宿主对象Window的一部分实现。

    JSEngine的起始阶段就是实例化一个Window对象。

    这也解释了(this === window) == true;

    属性

    预定义对象

    Object Array Function Boolean String Number Date RegExp Error EvalError RangeError ReferenceError SyntaxError TypeError URIError(作为函数原型的构造器)

    全局属性

    undefined NaN Infinity …

    全局函数

    编解码

    decodeURI()/decodeURIComponent()/encodeURI()/

    encodeURIComponent()/escape()/unescape()

    转换

    getClass()/Number()/String()/parseFloat()/parseInt()

    判断

    isFinite()/isNaN()

    执行

    eval()

    用户定义

    如var _temp_val = {}; // window._temp_val = {};全局变量混乱问题

    Tips:全局属性可以直接使用而不用[window. ] Object,同时除了用户定义以为的属性不能delete

      在JS引擎启动并实例化window对象时,JS原生对象和浏览器对象全部作为预定义对象放置在window中。

      当我们需要新建对象时,首先对象原型将会依据这些预定义对象被构建。

      举个小栗砸:(这里有一些构造器和原型链的应用,对后面章节的梳理有帮助。)

    1     var window.namespaceA = [];//声明命名空间
    2     namespaceA.A = {…};//声明函数,JS引擎加载时将A.prototype丢到堆内存
    3     var a = Object.create(A.prototype,{ …args });
    View Code

      JS引擎行为

        Object() = window.Object.prototype.constructor;//Object构造函数
        Function() = window.Function.prototype.constructor;//Function构造函数
        A() = window.namespaceA.A.prototype.constructor; // A构造函数
        a = new A({ …args});//以A对象为原型实例化引用a
        a.[[prototype]] = A.prototype;
    

    3. We Are Family

            该图总结并参考了ECMAScript5规范,如有错误请指出。

            部分对象请参考W3C教程:http://www.w3school.com.cn/jsref/index.asp

           

    Chapter two . User Object

      这一篇章进入对象的讲解,讲述在当前JS执行环境executable code(ECStack)中,如何创建一个原型实例对象。分配内存,形成作用域链与原型链。改变执行控制权并返回对象。

      总结:this new a object by prototype chain in ECStack, then this got return and leave ECStack.

    1.     Context – ECStack

      JS引擎执行过程是JS对象的生命周期的交替的过程,JS引擎解析执行。当执行子函数时,会将引擎操作的控制权让给子函数。子函数本身就是一个函数上下文。

        window.ECStack = [];//模拟执行环境,实际的执行环境包含window全局上下文

      备注:与执行上下文相关的作用域和参数对象在new小节讲解。

      执行环境ECStack内存时序结构图如图所示。

     

    2.     WHAT – object

      ECMAScript5规范丰富了用户自定义Object对象,提供了Object对象的属性特性。

     

      参考W3C教程:http://www.w3school.com.cn/js/pro_js_referencetypes.asp

      备注: JSON对象拓展,使用JSON. Stringify(/*Object*/)和JSON.parse(/*String*/)进行对象序列化,有时依赖引用对象的toJSON()方法。

    3.     TYPE – prototype chain

      JS是元解释型语言,当JS引擎执行JS代码时,会分析语法结构,并将得到的对象结构放置在堆内存中,称为原型。内存中的所有对象都包含一个属性[[prototype]]指针指向堆内存的原型,FireFox,Chrome等浏览器将该属性定义为__proto__可显示调用。

      酱:Foo.__proto__ à Foo.prototype

      同时原型对象存在原型,形成一条原型链。当JS引擎实例化window对象构造全局上下文this时,堆内存中生成如下原型链:

     

      图中每一个矩形都是一个原型对象。每个原型对象都有自己的构造器函数。

      酱:Foo.prototype.constructor == Foo();//注意这里是函数,只是JS允许写成Foo。

      我将JAVA与JavaScript做类比,虽然这样可能不太妥当。

    JavaScript概念

    Java概念

    预定义对象,如Object

    API对象,如java.lang.Object

    内核

    JVM

    内核初始化window时,原型对象Object.prototype加载到堆内存中

    JVM启动后,类信息Object.class加载到方法区中。

    Object()//Object.prototype. constructor构造函数

    Object构造函数

    var obj = new Object();

    Object obj = new Object();

    obj.__proto__ == Object.prototype

    obj.getClass() == Object.class;

      Constructor构造函数概念是一个动态概念,意味着运行时内核中的原型对象才包含构造函数。理解预定义对象,原型对象,原型对象构造函数和实例化对象后,需要引申一个概念:如何判定对象类型。

    关键字

    类型

    返回值

    说明

    typeof

    一元运算符

    字符串

    基本类型undefined string number Boolean

    引用类型object 函数类型 function。

    不返回null的原因:object JS类型值是存在32 BIT 单元里,32位有1-3位表示TYPE TAG,其它位表示真实值。object的标记位为000。而null标记位为00,最终体现的类型还是object..

    instanceof

    二元运算符

    布尔值

    判断除undefined null一个变量是否某个对象的实例,就是看该对象是否在该变量的原型链上。

    constructor

    函数

    构造函数

    var temp = obj.constructor.toString();

    temp.replace(/^function (w+)().+$/,'$1');

    prototype

    对象

    原型类型

    Object.prototype.toString.call(obj).slice(8,-1).toLowerCase();

      这时候我们可以把本节原型链内存结构图补齐啦:

     

      当查询对象的属性时,若该对象不存在该属性,则在该对象的原型链中向上查找对象原型是否存在该属性。若存在,则返回该属性,若直到原型为null时仍未找到该属性,返回undefined。

      javascript找属性就是找原型链最近的,找不着就找他爹。

    4.     WHO – this

      this是当前执行环境上下文的一个属性。JavaScript是单线程的,意味着JS内核执行JS代码切换上下文,this就会改变。若JS执行到代码片段A,说明包含片段A的上一级内容B正在被加载/实例化,那么B就是this。

      ECStack = { VO : {…}, this : thisValue};

      初始状态

        当浏览器加载请求页面时,卸载原始事件,解析并渲染HTML,驱动当前事件并绑定当前窗口对象。每个窗口都有自己的全局对象,当页面被加载后,window对象实例化并绑定this。

    1 function main(){
    2     window = new Window();
    3     this.call(window);//扩展this环境对象,其实只有函数对象才有call方法
    4 |
    View Code

      无状态

        这一块并没有过多的代码演示。个人有两点理解。

      1.   A调用方法a,则a方法内的this为A。
      2.   若A中没有a方法,则a方法内的this为A原型链上包含方法a最近的原型对象B。
    1 function Foo () {}
    2 Foo.prototype.foo = "bar";
    3 Foo.prototype.logFoo = function () { 
    4     eval("console.log(this.foo)"); //logs "bar" 
    5 }
    6 var foo = new Foo ();
    7 foo.logFoo();//foo调用logFoo方法,只有foo原型有logFoo方法,所以Foo.prototype为this
    View Code

        备注:判断a是否是A的自由属性,使用hasOwnProperty方法。

        备注:可以使用with,apply,call,bind方法连接this环境上下文,实现属性继承,这里不作过多说明。

    5.     DO – new

    Introduce

      new关键词实例化一个对象。对象实例化过程,对this的不断赋值,构造参数对象arguments,并维护实例对象的constructor属性。

    Step

      举个栗砸。使用构造函数自定义对象:

    1.   function A(/* number*/ id, /* string*/name){ this.id = id; this.name = name;}
    2.   var a = new A(1);//a.__proto__ = A.prototype

    分解步骤如下:

    •   执行1行时,加载A对象构造函数并放入内存,构造原型链

      1 A.prototype.constructor= A(){…}; 2 A() = new Function(); 3 Function() = new Object();  

    •   执行2行时,使用构造函数实例化a,并将A.prototype作用域给实例a

     1 Var a = new A(); 2 A.call(a, _args);//_args.id =1; 

    •   执行内存中构造函数A.prototype.constructor前,构造参数对象arguments对象

     1 arguments = A.prototype.slice.call(_args);;//传入参数 2 arguments.callee == this;//参数对象调用者为当前函数体 

      备注:arguments.length表示实际传参数,arguments.callee.length表示期望传参数

    •   执行内存中构造函数A.prototype.constructor时,根据参数赋值,继承原型链中的属性方法。
    1 a.id = arguments[1];//值为1
    2 a.name = arguments[2];//值为undefined
    3 a.constructor = A.prototype.constructor = A(){…};//继承
    •   返回值

     1 return a; 

    Ways

    新建对象方法

    描述

    备注

    使用JS内存中内置对象的构造方法

    var str = new String(“str”);

    只能实例化内置对象

    使用JSON字面量

    var o = {‘name’ :‘sapphire’};

    方便快捷的方式

    工厂模式

    自定义函数中创建对象并返回

    抽象方式

    构造函数模式

    function Foo(_args){…};

    var foo = new Foo(_args);

    常用方式

    方法无法共享

    原型模式

    function Foo(id){Foo.prototype.id = id};

    切断已存在实例与原型对象关系

    属性共享

    组合模式

    function Foo(){…};

    Foo.prototype = {fun : function(){}};

    构造函数模式构造属性

    原型模式构造方法

    6.     RESULT – return

      当构造函数无特殊声明return返回值时,返回实例化对象。

    Chapter three . closure

      函数闭包是一种技术,尤其被Lambdas语言广泛应用。Java中可以使用匿名函数的方式模拟闭包。

    1.     Introduce

      闭包 = 闭包函数 + 一组自由变量与名称绑定存储区映射,实现函数外部访问函数内部变量的技术。

      原理:如果一个函数包含内部函数,那么它们都可以看到其中声明的变量;这些变量被称为‘自由’变量。然而,这些变量可以被内部函数捕获,从高阶函数中return实现‘越狱’,以供以后使用。唯一需要注意的是,捕获函数必须在外部函数内定义。函数内没有任何局部声明之前(既不是被传入,也不是局部声明)使用的变量就是被捕获的变量。

      备注:Javascript中20%以上的博客是关于闭包概念的,这里不再赘述。

      弊端:应用时需分析函数变量,检查闭包是否会互相产生干扰,检查闭包的实例是否相同。

      利端:闭包函数实例化时只执行一次,将不需要暴露在外层环境的变量封装在内部,减少了外部变量。

    Chapter four . inherit

      在ECMAScript中,只支持实现继承而不支持签名继承(接口继承)实现继承基本是通过原型链继承。

    继承方式

    示例

    原型链继承

    Sub.prototype = new Super();

    构造函数继承

    function Sub(){ Super.call(this);}

    组合继承

    原型式继承

    Object.create(prototype)

    Chapter five . namespace

      以上我们接触了全局变量和this的概念,前端JS开发中,不规范的命名规则会导致JS全局变量混乱甚至冲突。

      下面这段代码示例规范变量命名空间

     1 var GLOBAL = {};
     2 GLOBAL.namespace = function(str){
     3   var arr = str.split('.');
     4   var start = 0;
     5   if(arr[0] == 'GLOBAL'){ start = 1; }
     6   for(var i = start; i < arr.length; i++){
     7     GLOBAL[arr[i]] = GLOBAL[arr[i]] || {};
     8     GLOBAL = o[arr[i]];
     9   }
    10 };

      假设a变量完成A功能中A1子功能。b变量完成A功能中A2子功能。c变量完成B功能。那么

     1 GLOBAL.namespace('A.A1'); A.A1.a = …; 2 GLOBAL.namespace('A.A2'); A.A2.b = …; 3 GLOBAL.namespace('B'); B.c = …; 

      同时,尽量在匿名函数中声明变量而非全局环境中,并为代码添加更多注释。

    Chapter six . reference

      火狐开发者JS文档     https://developer.mozilla.org/en-US/docs/Web/JavaScript/A_re-introduction_to_JavaScript

      IBM开发者社区       http://www.ibm.com/developerworks/cn/web/

      有关ECMA-262个人站点  http://dmitrysoshnikov.com/


    作者:牧伯流水
    World need ur smile O(∩_∩)O Copyright ©2015 Galaxias.Sapphi.REN

  • 相关阅读:
    ThreadLocal总结
    zookeeper学习笔记
    安装solr
    VirtualBox安装Centos7
    Solr学习
    Redis缓存会出现的问题?
    面试题目总结
    mysql分库分表
    Java内存模型
    HashMap在多线程中循环链表问题(jdk1.7)
  • 原文地址:https://www.cnblogs.com/Galaxias-Sapphi-REN/p/4831072.html
Copyright © 2011-2022 走看看