村庄
0 50

模型格式为:max

点击下载

0 50
() 全部评论
所有回复 (0)

热门

最新

  • Web3D编程 1 0 1 发布

    WebGL(Web Graphics Library)是HTML5规范中的一部分,可以用来渲染三维场景。
    WebGL源自OpenGL ES(OpenGL for Embedded Systems 是OpenGL三维图形API的子集)。

    示例代码// 创建canvas function createCanvas(width, height) { var canvas = document.createElement("canvas"); canvas.width = width || 300; canvas.height = height || 300; return canvas; } // 创建着色器 function createShader(webGL, source, type) { var shader = webGL.createShader(type); webGL.shaderSource(shader, source); webGL.compileShader(shader); return shader; } // 初始化webGL function initWebGL(canvas) { var webGL = canvas.getContext("webgl") || canvas.getContext("experimental-webgl"); if (!webGL) { console.log("当前浏览器不支持WebGL,请切换到支持WebGL的浏览器,比如Chrome"); return null; } else { return webGL; } } // 创建矩形 function drawRect(webGL, program) { var buffer = webGL.createBuffer(); webGL.bindBuffer(webGL.ARRAY_BUFFER, buffer); webGL.bufferData( webGL.ARRAY_BUFFER, new Float32Array([-1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0 ]), webGL.STATIC_DRAW ); // vertex data var positionLocation = webGL.getAttribLocation(program, "a_position"); webGL.enableVertexAttribArray(positionLocation); webGL.vertexAttribPointer(positionLocation, 2, webGL.FLOAT, false, 0, 0); } // 入口函数 function init() { var canvas = createCanvas(), VER_SHADER_SRC = "attribute vec4 a_position;\n" + "void main() {\n" + "gl_Position = a_position;\n" + "}", FRAG_SHADER_SRC = "precision mediump float;\n" + "void main() {\n" + "gl_FragColor = vec4(1, 0, 0.5, 1);\n" + "}"; document.body.appendChild(canvas); var webGL = initWebGL(canvas); if (webGL) { webGL.clearColor(0.0, 0.0, 0.0, 1.0); webGL.clear(webGL.COLOR_BUFFER_BIT | webGL.DEPTH_BUFFER_BIT); var vertex = createShader(webGL, VER_SHADER_SRC, webGL.VERTEX_SHADER), frag = createShader(webGL, FRAG_SHADER_SRC, webGL.FRAGMENT_SHADER); var program = webGL.createProgram(); webGL.attachShader(program, vertex); webGL.attachShader(program, frag); webGL.linkProgram(program); webGL.useProgram(program); drawRect(webGL, program); webGL.drawArrays(webGL.TRIANGLES, 0, 6); } }代码说明

    WebGLShader(WebGL着色器),WebGL只关心两件事:位置坐标与颜色,所有WebGL程序都是基于这两个元件来构造的,常用的方法是使用两个着色器来实现:

    顶点着色器(vertex shader):返回位置坐标

    像素着色器(fragment shader):返回颜色

    vec4()构造函数用于生成颜色,示例代码中vec4(1, 0, 0.5, 1)的数字的含义:

    第一个1:代表红色

    0:代表绿色

    0.5: 代表蓝色

    第二个1:代表alpha

    在WebGL中颜色的取值范围是0到1.

    具体方法:

    createShader:生成相应类型的WebGLShader

    shaderSource:生成GLSL的回调函数

    compileShader:编译着色器

    WebGLProgram(WebGL环境)着色器编译完后,需要将着色器链接到WebGLProgram:

    createProgram:创建一个WebGL环境

    attachShader:附加着色器

    linkProgram:将着色器连接到WebGL环境中

    提供数据:

    getAttribLocation:从当前环境中寻找定义的属性,即查找示例中初始化顶点着色器时定义的a_position。

    createBuffer:创建buffer(缓存区)

    bindBuffer:声明变量,并指向上一步创建的buffer

    bufferData:赋值数据到指定变量,比如示例中的ARRAY_BUFFER

    清空canvas:

    clearColor:

    clear:

    使用创建的WebGL环境:将当前的WebGL环境告知GPU程序

    useProgram:

    告知WebGL从buffer中获取数据的方式:

    enableVertexArribArray:激活属性

    vertexAtrribPointer:获取数据

    绘制图形:WebGL执行GLSL程序

    drawArrays:这是WebGL提供的一个渲染函数

    效果

  • Web3D编程 1 0 1 发布
    值和单位

    CSS中的值有以下几种类型:

    颜色值 
    颜色值的应用场合有:背景颜色,边框颜色,字体颜色等长度值 
    长度值的应用场合有:元素的大小,边框颜色,字体颜色等时间值角度值颜色值颜色取值

    CSS中颜色取值主要由以下几种方法: 

    CSS命名颜色
    在CSS2.1中,CSS规范定义了17个颜色名。用RGB指定颜色
    可以使用0~255取值或者0%~100%。长度值

    长度值的单位有绝对单位和相对单位两种:

    绝对单位in 英寸,cm 厘米mm 毫米pt 磅,1pt等于1/72 英寸pc 派卡,印刷术语相对单位em 1em等于当前的字体尺寸。如果某元素以12pt显示,那2em的设定值就等同于24pt。em非常适合于需要对用户所使用字体大小进行自动适应的场合。ex px 像素% 百分比继承

    继承是一种规则,它允许样式不仅应用于某个特定html标签元素,而且应用于其后代。比如下面代码:如某种颜色应用于p标签,这个颜色设置不仅应用p标签,还应用于p标签中的所有子元素文本,这里子元素为span标签。

    优先级

    有的时候同一个元素同时被设置了多个不同的CSS样式代码,那最终是哪一个CSS样式有效呢?我们来看一下面的代码:

    CSS: p {color:red;} .hudao {color:green;} #hello {color:yellow;} HTML: <p id= "hello" class="hudao">您好,这里是互道科技</p>

    对<p>中的文本,有三个样式分别定义了三种不同的颜色,red(红色),green(绿色)和yellow(黄色),但文本会显示成yellow(黄色)。 这是因为CSS样式是有优先级的,按照CSS的优先级规则,通过ID指定的样式最终获得胜出。 下面对CSS样式优先级计算时的三大要素:权值,特殊性和层叠分别进行说明:

    权值

    CSS样式的优先级首先是通过权值来实现的,权值高的样式将被浏览器优先采用。 权值被分为以下四个等级:

    等级1 内联样式(如:style=””)属于第1等级,权值是1000。等级2 ID选择器(如:#id1)属于第2等级,权值为100。等级3 类,伪类和属性选择器(如:.cls1)属于第3等级,权值为10。等级4 元素和伪元素选择器(如:.div)属于第4等级,权值为1。特殊性

    通过!important关键字可以将样式的权值人为提升到最高级。

    层叠

    层叠就是同一个元素,当有相同权重的样式存在时,会根据这些CSS样式的前后顺序来决定,处于最后面的CSS样式会被应用。

  • Web3D编程 1 0 1 发布

    CSS的基本目标是让浏览器以指定的特性去绘制页面元素,这些是由属性和属性值的定义来实现的。

     在CSS中,一个属性与值的键值对被称为CSS声明,多个CSS声明组成CSS声明块,CSS声明块则关联选择器则形成CSS规则,CSS规则的集合则最后构成整个样式表。

    属性和值

    属性(property)是一个标识符,表示某个CSS特性,比如:颜色,字体,大小等。

    值(value)则描述了浏览器引擎如何处理该特性。每个属性都包含一个有效值的集合,它有正式的语法和语义定义,被浏览器引擎实现。

    样式表基本上就是由一系列的这些属性和值构成的键值对所组成。

    声明

    声明(declaration)是一个属性与值的键值对,属性与值之间以:(冒号)分隔。

    border: 1px solid hsla(0,0%,0%,.5);声明块

    声明块(declaration block)包含一条或多条声明,声明块里的各个声明之间用分号分离。 声明块被包含在一对大括号{和}里面,声明块可以嵌套。

    { border: 1px solid hsla(0,0%,0%,.5); border-radius: 2px; width:235px; box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3); margin:36px auto; }规则

    规则(Rule)由两个主要的部分构成:选择器(Selector),声明块。选择器用来选择页面多个元素的条件,声明块定义对符合条件的页面元素如何设置属性。

    .search-bar { border: 1px solid hsla(0,0%,0%,.5); border-radius: 2px; width:235px; box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3); margin:36px auto; }at规则

    规则是CSS语句的一种类型,其作用是将一组CSS声明与用选择器定义的条件相关联。
    除规则之外,CSS中还有其他比如定义字符集,导入其它的外部样式表,字体等比较特殊的命令,这些是通过at规则语句来实现的。
    at规则(at-rules)。以@开始,随后是标识符,一直到以分号或右大括号结束。每个at规则由其标识符定义,可能有它自己的语法。
    at规则涵盖了meta信息(比如 @charset @import),条件信息(比如@media @document), 描述信息(比如@font-face)。

    语句

    上面的规则和at规则在CSS中被统称为CSS语句。 语句以非空格的字符开头,以第一个反花括号或分号结束。

    注释代码

    CSS里可以写注释代码,CSS注释的开始使用/*,结束使用*/。

    /*下面是检索条的样式定义*/ .search-bar { }
  • Web3D编程 1 0 1 发布

    每个可视的HTML元素在页面上都会被渲染成一个矩形区域,通常被称为盒子模型。

     区域组成

    盒子模型主要定义四个区域:

    内容(content)、 内边距(padding)、 边框(border) 外边距(margin)。

    容器元素的客户区域包含内边距(padding)和内容(content),但不包含边框(border)。因此在position=absolute的情况下,left和top属性指定的是子元素的外边距(margin)的左上角相对于父元素的内边距(padding)左上角的位置。

    类型

    盒子模型有两种类型:传统的content-box和CSS3之后才导入的border-box,由box-sizing属性定义。虽然border-box更位直观,但因为兼容的原因,缺省值仍然是传统的content-box。

    content-box 
    width和height定义的是内容(Content)部分的宽度和高度而不是整个盒子的高度,内边距(padding)、边框(border)和外边距(margin)依次加在外面。背景会填充padding和content部分。

    border-box 
    width和height定义包含边框(border),内边距(padding)和内容(content),但不包含外边距(margin) 

  • Web3D编程 1 0 1 发布

    JavaScript是一门非常简练并且功能强大的脚本语言,其机制和原理具有非常与众不同的特点。

     一切都是对象

    JavaScript中,数值、字符串、数组、函数等一切都属于对象(Object)。

    与其他语言一样,JavaScript仍然支持如下所示的原始数据类型及相应的字面量定义方式:

    数值型 如:3、-2、3.14字符串型 如:“Hi,world!”、“china”布尔型 如:true、false

    但与一般语言不同的是,基于一切都是对象的思想,JavaScript语言引擎会在程序执行时对以上原始数据类型自动创建相应的包装对象,从而可以象对象一样调用其对象方法。

    数值型 –> Number字符串型–> String布尔型–> Boolean

    例如,String对象都拥有返回字符串长度的length属性,如果访问“Hi,world!”.length、 将获得返回值9。

    而且,JavaScript的对象模型非常简单,JavaScript中的对象就是个键/值对的集合。

    这里的「键」不限于字符串类型,也可以是数值或其他对象。

    事实上,JavaScript中的数组(Array),本质上也是一个键/值对的集合,自然索引也是作为属性名(键)存在的。

    对象的属性对象基于原型

    Java或C++等基于类的面向对象语言都包含有两个基本概念:类(Class)和实例(Instance)。类是一个抽象的模板,比如Employee类,而实例是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的数据可能不同。

    而JavaScript的面向对象的基本概念是原型(Prototype)。对象不分类和实例,但对象可以将其他的对象作为自己的原型,然后根据需要定义独自的属性。

    运行期,JavaScript语言引擎会为所有的对象维护一个原型链,原型链的顶端是Object对象。

    当对象被访问属性时,语言引擎会先查找对象本身,对象本身不存在该属性时,然后按照原型链从下到上依次查找。这就是JavaScript语言中的继承机制。

    ECMAScript6虽然也引入了类(Class),但这并不意味着JavaScript在原型机制之外导入了一个类似于C++「类」的新机制。

    ECMAScript6中的类仅仅是提供了一个更简单明了的语法方法用于定义构造函数,本质上就是构造函数,这也是其常常被说成是语法糖的缘由。

    对象的原型完全动态语言

    JavaScript是一个完全动态的语言,这里包含以下三个意思:

    JavaScript是一个动态类型语言JavaScript是一个弱类型语言JavaScript是一个动态编程语言

    其中,动态类型语言和动态编程语言是两个不同的概念,本身并没有关联性。

    动态类型语言

    动态类型语言(Dynamically Typed Language)指的是在运行期而不是在编译期对类型进行检查的一类语言。

    备注: 作为解释语言的JavaScript等语言,其编译期可以理解成将脚本加载并转换为内部可执行代码这样一个过程。

    属于动态类型语言的还有PHP、Ruby、Python等。

    与动态类型语言相对的就是静态类型语言(Statically Typed Language),C#、Java、TypeScript等就属于这类语言。

    弱类型语言

    弱类型语言具有以下特点:

    变量没有类型,可以被赋予不同类型的值计算时不同类型的值会自动进行转化

    同属动态类型语言的PHP、Ruby、Python中,PHP、Ruby也属于弱类型语言,而Python因为变量在第一次被赋值后,其类型便已被确定下来,另外计算时必须强制指定类型转化,因此通常被认为是强类型语言。

    动态编程语言

    动态编程语言(Dynamic Programming Language)指的是能够在运行中动态改变其程序结构,包括增加新的函数、类型等的一类语言。 JavaScript里可以在函数里嵌套函数,如下例所示,嵌套函数f2就是在外面的f1()函数被调用时才创建的,这也是JavaScript闭包实现的基础。

    function f1(x) { function f2() { return x; } return f2; } f1(3);部分支持函数编程

    JavaScript部分具有函数语言的特点,函数语言本身可能比较复杂,但对于JavaScript来说,您只要掌握以下3点就可以了:

    所有的函数都有返回值函数没有副作用函数也可以当作值进行处理函数都有返回值

    所有的函数都返回值,如果没有明确的代码实现,函数的返回值就是undefined。

    函数没有副作用

    函数没有副作用,包含以下两点:

    函数不应改变外部的变量值函数不应改变引用传递的参数的值函数也是值

    函数也是对象,一种可以被执行的特殊对象。 函数能够当作值进行处理,因此関数的参数可以是另外一个函数,函数的返回值也可以是一个函数。

    闭包

    闭包是函数式语言中一个非常重要和强大的功能。JavaScript在函数对象被创建时会把当时的执行环境保存起来,形成一个闭包作用域,函数每次执行时都能够访问这个作用域中的变量。因此,闭包通常用来创建内部变量,使得这些变量不能被外部随意修改,同时又可以通过指定的函数接口来操作。

    代码的执行作用域和闭包
  • Web3D编程 1 0 1 发布

    在JavaScript中,所有的对象都是一组属性的集合,属性可以是数值,字符串等原始类型,也可以是函数,或者是其他对象。 

    属性的类型

    JavaScript中的属性有两种类型:数据属性和访问器属性。

    数据属性

    数据属性可以看成是直接封装了一个内部变量,内部变量中存放了该属性的值。 当对某个对象尚未存在的属性进行赋值时,该属性将会作为数据属性被自动创建。

    var o = {}; o.prop1 = "value1";

    上面的代码中,对象o的属性prop1即会在被赋值时自动创建。

    数据属性也可通过Object.defineProperty方法和value定义来创建。

    var o = {}; Object.defineProperty(o,"prop1",{ value : "value1", writable : true }访问器属性

    访问器属性类似于C#,Ruby,Delphi等语言中的属性,内部可以不用直接关联一个数据变量,而是为属性的读取和更新分别提供了一个相应的getter方法和setter方法。 访问器属性必须通过Object.defineProperty或其他类似的方法事先进行定义。

    var o = {}; Object.defineProperty(o,"prop1",{ get: function(){ if (this._prop1 === undefined) { this._prop1 = 1; } return this._prop1; }, set : function(s){ if (typeof s == "string"){ this._prop1 = parseFloat(s); } else if ( typeof s == "number") { this._prop1 = s; } else { throw new Error("invalie parameter!"); } } }

    有了get方法,我们就可以在属性第一次被访问时才去进行初期化处理,而有了set方法,我们就可以追加对赋值进行类型转化等很多数据属性没法实现的程序逻辑。

    属性的特性

    ES5开始,JavaScript为属性提供了三个特性用于描述其各种特征。特性是内部值,不能直接访问。属性的特性会有一些默认值,要修改特性的默认值,必须使用Object.defineProperty方法。

    下面依次对这些特性进行说明

    configurable

    configurable特性定义是否可以通过delete操作符来删除属性,默认值是true。

    var o = {}; o.p1="value1"; console.log(o.p1); // value1 delete o.p1; console.log(o.p1); // undefine

    如以上代码所示,属性被通过delete操作符删除之后再去访问就已变成未定义了。 然后,我们可以把属性的Configurable特性设置为false来防止属性删除。

    var o = {}; o.p1="value1"; console.log(o.p1); // value1 Object.defineProperty(o, "p1", { configurable: false }) delete o.p1; console.log(o.p1); // value1enumerable

    enumerable特性定义是否能够通过for…in语句来枚举出属性,默认是true

    writable

    writable特性定义表示属性值是否可以修改,默认为true。

    属性的继承

    属性可以通过对象的原型链进行继承。

    var objA = new Object(); objA.prop1 = 10; function Func1() {} Func1.prototype = objA; var objB = new Func1(); function Func2() {} Func2.prototype = objB; var objC = new Func2(); console.log(objC.prop1); // 10

    上面的代码中,objC本身没有prop1属性,因此访问objC.prop1时,JavaScript将会按照objC—>objB—>objA的原型链进行顺序查找,最后从objA中取出该属性值。

    objC.prop1 = 20;

    这时如重新将objC.prop1进行赋值,并不会影响到objB和objA,而是objC自身会被自动创建一个同名的数据属性。

    console.log(objC.prop1); //20 console.log(objB.prop1); //10 console.log(objA.prop1); //10属性的键值

    JavaScript里对象的属性是以键/值对的形式存在的,这里的「键」不限于字符串类型,也可以是数值或其他对象。

    事实上,JavaScript中的数组(Array),本质上也是一个键/值对的集合,数值类型的自然索引也是作为属性名(键)存在的。 请看以下代码:

    //数值也可以是属性名(键) var a = [0,9,2]; console.log(2 in a); // 输出:true console.log(3 in a); // 输出:false console.log(a.1); // 语法错 //任意对象也可以是属性名(键) var d1 = new Date("2012/01/01"), d2 = new Date("2013/03/01"), o = { }; o[d1] = "2012/01/01"; o[d2] = "2013/03/01"; console.log(o[d1]); // 2012/01/01 console.log(o[d2]); // 2013/03/01 //下面代码,d1,d2在JavaScript语法里被当作变量名的字符串处理, console.log(o.d1); // undefined console.log(o.d2); // undefined

    字符串以外的值虽然也可以作为属性的键值,但因为JavasSript语法只允许字符串为变量名,所以不能以a.1或o.d1这样的方式,而只能以a[1]或o[d1]的方式访问对象的属性。

    对象限制

    ES5中提供了一系列限制对象被修改的方法,按限制程度由低到高,依次有禁止扩展,密封,冻结三种模式。当然,即使是冻结模式,访问器属性的set方法仍然可正常动作,表现出来就是该属性值仍可修改。

    禁止扩展对象

    通过Object.preventExtensions()方法可以禁止将对象进行扩展,禁止扩展后的对象无法:

    添加新的属性

    但可以:

    删除已有的属性改变已有属性的特性修改已有数据属性的值(如果该属性可写)修改已有访问器属性的值(如果有set方法)密封对象

    通过Object.seal方法可以将对象进行密封,密封后的对象无法:

    添加新的属性删除已有的属性改变已有属性的特性

    但可以

    修改已有数据属性的值(如果该属性可写)修改已有访问器属性的值(如果有set方法)冻结对象

    通过Object.freeze方法可以将对象进行冻结,冻结后的对象无法:

    添加新的属性删除已有的属性改变已有属性的特性修改已有数据属性的值(即使该属性可写)

    但可以

    修改已有访问器属性的值(如果有set方法)相关方法汇总

    属性的相关方法都是以Object的静态方法或原型方法的形式提供的,下面简单的做一下汇总:

    属性定义相关Object.create() 创建对象的同时定义该对象的属性Object.defineProperty() 定义一个属性Object.defineProperties() 定义一组属性Object.getOwnPropertyDescriptor() 获取属性定义信息属性访问相关Object.keys() 获取对象的所有属性名,仅限于可枚举的自身属性Object.getOwnPropertyNames() 获取对象的所有属性名,包括可枚举和不可枚举,仅限自身属性Object.prototype.hasOwnProperty() 判断对象自身是否拥有某个属性对象限制相关Object.preventExtensions() 限制对象扩展Object.isExtensible() 判断对象是否可以扩展Object.seal() 密封对象Object.isSealed() 判断对象是否被密封Object.freeze() 冻结对象Object.isFrozen() 判断对象是否被冻结
  • Web3D编程 1 0 1 发布

    JavaScript是一个基于原型的面向对象的语言。 本文着重于对原型的实现机制进行剖析和说明。

     原型链的实现

    JavaScript里所有的对象都有一个名为__proto__的属性,这个属性里面存放的就是对象所参照的原型对象的引用。

    __proto__中的对象连在一起就构成了一个原型链,链的顶端就是Object.prototype对象,Object.prototype的__proto__属性值则是null
    __proto__属性被包含在ECMAScript6之中,但之前基本上已被大多数浏览器厂商所支持。
    通过Object.getPrototypeOf()可以获得指定对象的原型对象,这也是被推荐的使用方法。但__proto__属性是可读写的,这也意味着程序可以通过该属性动态的改变对象的原型对象。

    原型的自动设置

    当通过构造函数创建新对象时,JavaScript会自动将构造函数的prototype属性值设置到新对象的__proto__属性里。

    作为示例,我们首先声明一个类(构造函数)Person

    var Person = function(name) { this.name = name; }; Person.prototype.getName = function() { return this.name; };

    然后我们创建一个Person的对象。

    var tom = new Person("Tom");

    上面创建Person对象的代码与下面的程序逻辑是等价的,事实上JavaScript也是这样执行的。

    var tom = new Object(); tom.__proto__ = Person.prototype; tom = Person.call(tom,"Tom");属性的继承

    当访问对象的属性时,JavaScript会通过遍历原型链进行查找,直到找到给定名称的属性为止。

    如果查找进行到原型链的顶部-Object.prototype仍然没有找到指定名称的属性时,就会返回undefined。

    由于这个查值过程是一个遍历过程,所以当属性的值越靠近顶层,查找性能就会越低,而当值靠近底层时,查找性能就会高很多,所以在编写复杂的应用时,一定要提防原型链过长而带来的性能问题。

    而设值对象属性则不会遍历原型链,而是直接将属性添加到该对象自身,并不影响到原型链中的对象。

    //接上面的例子 //下面这条语句直接取的是tom对象的属性值 console.log(tom.name); // 输出:Tom //下面这条语句执行的是tom.__proto__(引用的是Person.prototype)的getName方法 tom.getName(); // 输出:Tom
  • Web3D编程 1 0 1 发布

    本文着重于对JavaScript代码的执行机制进行剖析和说明。 

    代码类型

    在JavaScript中,可执行的JavaScript代码分三种类型: 

    函数体代码(Function Code) 
    即用户自定义函数中的函数体JavaScript代码。全局代码(Global Code) 
    即全局的、不在任何函数里面的代码,包括写在文件以及直接嵌入在HTML页面中的JavaScript代码等。 动态执行代码(Eval Code) 
    即使用eval()函数动态执行的JavaScript代码。 

    不同类型的代码其执行机制也有所不同。

    线程模型JavaScript引擎线程

    JavaScript语言规范没有包含任何线程机制,客户端的JavaScript也没有明确定义线程机制,但浏览器端的JavaScript引擎基本上还是严格按照”单线程”模型去执行JavaScript代码的。
    究其原因,应该还是为了简单吧,因为JavaScript的主要用途是与用户交互以及操作DOM,如果采用多线程,将会带来很复杂的同步问题。

    JavaScript引擎是基于事件驱动的,引擎维护着一个事件队列,JavaScript引擎线程所作的就是不断的从事件队列中读取事件,然后处理事件,这个过程是循环不断的,所以整个的运行机制又称为事件循环(Event Loop)。

    虽然HTML5提出的Web Worker允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM,所以可以说,Web Worker并没有改变JavaScript单线程的本质。

    浏览器的其他线程

    JavaScript引擎是单线程的,但浏览器本身是多线程的,JavaScript引擎线程只是浏览器里的一个线程,除此之外,浏览器通常至少还有以下四类线程:

    GUI渲染线程 
    在JavaScript中,GUI渲染操作也是异步的,DOM操作的代码会在GUI渲染线程的事件队列中生成一个任务,GUI渲染处理由GUI渲染线程而不是JavaScript引擎线程执行。
    但需要注意 GUI渲染线程与JavaScript引擎线程是互斥的,当JavaScript引擎线程执行时GUI渲染线程会被挂起,而GUI渲染线程执行时,JavaScript引擎线程肯定不在执行状况。用户交互线程 
    当一个用户入力事件(鼠标点击,键盘入力等)被触发时该线程会把事件添加到JavaScript引擎线程的事件队列的队尾,等待JavaScript引擎线程的处理。网络通信线程 
    网络通信线程负责网络通信,并且在服务器回复之后会把事件添加到JavaScript引擎线程的事件队列的队尾,等待JavaScript引擎线程的处理。定时器线程 
    定时触发(setTimeout 和 setInterval)是由浏览器的定时器线程执行定时计数,然后在定时时间结束时把定时处理函数的执行代码插入到 JavaScript引擎线程的事件队列的队尾,等待JavaScript引擎线程的处理。 
    所以用这两个函数的时候,实际的执行时间是大于指定时间的,并不保证能准确定时。执行上下文

    不仅仅是JavaScript,解释性语言都存在执行上下文(execution context,也称执行环境,运行上下文)这样一个概念。

    执行上下文定义了执行中的代码有权访问的其他数据,决定了它们各自的行为。

    分类

    执行上下文大致可以分为两类:

    全局上下文(global context) 
    最外围的一个执行上下文,全局上下文取决于执行环境,在浏览器中则是window。局部上下文(函数执行上下文) 
    每个函数都有自己的执行上下文,当执行进入一个函数时,函数的执行上下文就会被推入一个执行上下文栈的顶部并获取执行权。
    当这个函数执行完毕,它的执行上下文又从这个栈的顶部被删除,并把执行权并还给之前执行上下文。这就是JavaScript程序中的执行流。


    而由eval()函数动态执行的代码运行在调用者的执行上下文之中,不会产生新的执行上下文。

    与作用域的关系

    执行上下文与作用域很容易被混淆成同一个东西,事实上两者的概念是完全不同的。

    以函数为例,函数的执行上下文是完全与函数代码运行相关联的动态存在,相关代码运行结束了,与之相关联的执行上下文也就被释放了,而作用域更多的是一个静态的概念,如闭包作用域就与代码是否正在执行没有关系。

    执行上下文与作用域的关联是:执行上下文会为执行中的代码维护一个作用域链,里面包含了代码可以访问的各个名字对象,当代码中出现访问某个标识符(变量名,函数名等),JavaScript引擎会根据这个作用域链顺序进行查找,如果存在则直接返回该对象,如果整个链里都不存在则会产生异常。

    构成

    执行上下文只是一个抽象概念,在具体JavaScritp引擎实现中,它会被表示为一个至少包含以下三个属性的内部对象:

    变量对象(Variable Object) 
    环境中定义的所有变量和函数(函数声明,函数形参)都保存在这个对象中。作用域链 
    一个由变量对象组成单向链表,用于变量或其他标识符查找,本质上,它是一个指向变量对象的指针列表,它只引用但不实际包含变量对象。 
    详细说明请参考执行上下文的作用域链this this被赋予函数所属的Object,具体来说:当函数被作为某个对象的方法被调用时,this代表该对象。全局函数和匿名函数里的this代表全局对象的window,但如果是strict模式,this则是undefined。构造函数中的this代表构造函数所创建的对象。apply()和call()方法在参数里明确指示函数执行时的this对象。流程

    在JavaScript中,程序代码是在执行上下文环境里被执行的,这包括两个阶段:

    为代码创建执行上下文 包括创建变量对象创建arguments对象,初始化参数名称和值扫描代码中的函数声明,将该函数对象放入变量对象扫描代码中的变量声明,将该变量对象放入变量对象,这个阶段变量的赋值语句并不执行,所以所有变量的值都是undefined初始化作用域链判断this对象执行代码 在当前上下文上解释执行代码

    从以上记述可以看到, 函数执行之前,函数的代码首先会被全部扫描,内部声明的函数,变量不分位置,全部事先登记到执行上下文的变量对象里。这为JavaScript语言带来了一个提升(Hoisting)的概念,即后面定义的名字,前面的代码也可访问。

    示例代码如下:

    (function() { log(); //正常输出hello,因为下面定义的log()函数的作用域被提升到顶端 v(); //异常,因为下面定义的v变量的作用域虽被提升到顶端但值为undefined function log() { console.log('hello'); } var v = log; }()); 异步处理

    JavaScritp的异步处理是通过回调函数实现的,即通过事件队列,在主线程执行完当前的任务,主线程空闲后轮询事件队列,并将事件队列中的任务(回调函数)取出来执行。

    异步处理大致有以下几大类型,不同的异步处理由不同的浏览器内核模块调度执行,调度会将相关回调添加到事件队列中。

    DOM事件回调定时触发回调网络通信回调Promise回调

    其中,Promise的优先级最高,排在其他所有类型的异步处理之前,而Promise以外的异步处理之间并没有优先级差别。