13518219792

建站动态

根据您的个性需求进行定制 先人一步 抢占小程序红利时代

全栈必备JavaScript基础

1995年,诞生了JavaScript语言,那一年,我刚刚从大学毕业。在今年RedMonk 推出的2017 年第一季度编程语言排行榜中,JavaScript 排第一,Java 第二,Python 反超 PHP 排第三,PHP 第四,C# 和 C++ 并列第五。RedMonk 排名的主要依旧是各种编程语言在 Stack Overflow 和 GitHub 上的表现,比如编程语言在 Stack Overflow 上的讨论数量,在 GitHub 上的代码量等。尽管有一定的片面性,还是说明了JavaScript 应用的广泛性。从全栈的角度看,Javascript 是必备的一种编程语言。

网站建设公司,为您提供网站建设,网站制作,网页设计及定制网站建设服务,专注于企业网站设计,高端网页制作,对成都报废汽车回收等多个行业拥有丰富的网站建设经验的网站建设公司。专业网站设计,网站优化推广哪家好,专业成都网站推广优化,H5建站,响应式网站。

ECMAScript 和 JavaScript 的关系

JavaScript 诞生于Netscape,但是在1996年,微软发布了与JavaScript 兼容的JScript,面对兼容和发展的需要,网景公司的先贤们努力加入了 ECMA International 标准化组织,致力于JavaScript 的标准化,命名为ECMAScript。后来,由于历史的原因, JavaScript标准的开发主体变成了Mozila基金会。

简单地,ECMAScript 是JavaScript语言的标准规范,就像C++的标准相对于C++语言那样。

JavaScript 是怎样的语言

在mozilla 开发者网站上是这样描述JavaScript的:

JavaScript (JS) is a lightweight interpreted or JIT-compiled programming language with first-class functions.

意思是说JavaScript 是一个轻量级解释或即时编译的函数式语言,里面有很多的概念,轻量、解释、编译、即时编译、函数式。在老码农看来,简单起见,理解为扩展语言较为方便。

一般的编程语言都有着自己相对独立的执行环境,但是JavaScript的执行环境依赖在宿主环境中,宿主环境尤其是客户端的宿主环境提供了更多统一的环境变量,比如浏览器中的window,document等。实际上,JavaScript 和DOM 是可分的,对于不同的运行环境,有着不同的内置宿主对象。JavaScript作为扩展语言在内置的宿主环境中运行,全局对象在程序启动前就已经存在了。

JavaScript的时空基础

从空间观的角度看,JavaScript包括数据结构,操作符,语句与表达式,函数;从时间的角度看,包括作用域,处理方式,模块与库。

数据结构

JavaScript 中包含的六种基本类型:

其它全是对象。值是有类型的,变量是没有类型的,类型定义了值的行为特征,变量在没有持有值的时候是undefined。 JavaScript对值和引用的赋值/传递在语法上没有区别,完全根据值的类型来判定。

对于对象的属性和方法而言,全局变量和全局函数是全局对象的属性,全局对象相当于宿主对象的根对象。需要注意是属性的属性中那些不可变对象的实现方式:

  1. 对象常量: 结合writable和configurable:false 可以创建一个真正的常量属性
  2. 禁止扩张:Object.preventExtensions(..)来禁止一个对象添加新属性并保留已有属性
  3. 密封: 在 Object.seal(..) 后不能增,删,改 该属性
  4. 冻结: Object.freeze(..) 会禁止对于对象本身及任意直接属性的修改

数据类型的判定可以通过 contructor,instanceof, isPrototypeOf等方法实现,对于鸭子类型的判定还可以使用 in 的相关操作。符号并非对象,而是一种简单标量基本类型。

JavaScript 中的强制类型转换总是返回基本类型值,将对象强制转换为String 是通过ToPrimitive抽象操作完成的,而toJSON()是返回一个能够被字符串化的安全的JSON值。

操作符

操作符是空间元素连接的纽带之一,JavaScript操作符包括算术,连接,相等,比较,逻辑,位,类型判断,条件,new,delete, void,",", ".", "[]"等。

在JavaScript中以操作符进行操作往往都附带着类型转换。

一元运算符+ 是显式强制类型转换,而~是先转换为32位数字,然后按位反转。|| 和&& 更应该算是选择器运算符,其返回值不一定是布尔值,而是两个操作数其中的一个值。一般先对第一个操作数进行toBoolean强制类型转换,然后再执行条件判断。例如:a||b 理解成a?a:b 更通俗。对&& 而言,如果第一个是真值,则把第二个作为返回值,a&&b 理解成a?b:a 。

== 和=== 都会对操作数进行类型检查,并执行隐性类型转换,需要注意的是:

这里是Github上关于各种相等性的矩阵:

语句与表达式

操作符与变量/常量等连接形成了语句和表达式,例如表达式a+1中的null 被强制转换为0。 语句包括声明与块,控制语句有判断,循环,break,continue,return,异常等。每个语句都有一个结果值,哪怕是undefined。

正则表达式是非常重要的一类表达式,主要使用RegExp类,执行方法test效率高,exec 会得到一个结果对象的数组。

逗号运算符可以把多个独立的表达式串联成一个语句,{ }在不同情况下的意思不尽相同,作为语句块,{ ..} 和for/while循环以及if条件语句中代码块的作用基本相同。{a,b} 实际上是{a:a,b:b}的简化版本。

try..catch..finally 中,如果finally中抛出异常,函数会在此处终止。需要注意的是,如果此前try中已经有return设置了返回值,则该值会被丢弃。finally中的return也会覆盖try和catch中的return的返回值。

函数与作用域

函数就是具有运算逻辑的对象,匿名函数不利于调试,回调函数是一种控制反转。所有的函数(对象)都具有名为prototype的属性,prototype属性引用的对象是prototype对象;所有的对象都含有一个隐式链接,用以指向在对象生成过程中所使用的构造函数的prototype对象。

匿名函数没有name 标识符,具有如下缺陷:

  1. 代码更难理解
  2. 调试栈更难追踪
  3. 自我引用(递归,事件(解除)绑定,等)更难

如果function是声明的第一个词,那就是函数声明,否则就是函数表达式。立即执行函数表达式形如:(function …)( )

时空密不可分,作用域是时空连接的纽带之一。作用域包括全局,函数,块级作用域。作用域是根据名称查找变量的一套规则,遍历嵌套作用域链的规则简单:引擎从当前执行作用域逐级向上查找。闭包可以理解为具有状态的函数。

函数作用域指属于这个函数的全部变量都可以在整个函数的范围内使用或复用。块作用域形如 with, try/catch, ES6 引入了let,const等。

动态作用域并不关心函数和作用域是如何声明以及在何处声明的,只关心它们从何处调用的。词法作用域是定义在词法分析阶段的作用域,词法作用域查找会在第一个匹配的标识符时停止。作用域链是基于调用栈的,而不是代码中的作用域嵌套。ReferenceError 是与作用域判别失败相关,而TypeError则是作用域判别成功,但是对结果的操作非法或不合理。

this 提供了一种优雅方式来隐式“传递”一个对象引用。 this 即没有指向函数的自身,也没有指向函数的作用域,是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用。如果分析this绑定的话,可以使用调试工具得到调用栈,然后找到栈中的第二个元素,就是真正的调用位置。

this 的绑定规则有:

  1. 默认绑定:独立的函数调用,严格模式不能将全局对象用于默认绑定
  2. 隐式绑定:把函数调用中的this 绑定到函数引用中的上下文对象
  3. 显式绑定:通过call()和apply()方法可以直接指定this的绑定对象。其中,硬绑定是一种显式的强制绑定,ES5中提供了内置方法Function.prototype.bind, API中调用的上下文和bind的作用一样。
  4. new 绑定,构造函数只是一些使用new操作符调用的函, 使用new 来调用函数的操作过程大致如下:

如果同时存在多种绑定,那么绑定的优先级大致如下:

  1. 由new调用绑定到新创建的对象
  2. 由call 或者apply(或bind)调用绑定到指定的对象
  3. 由上下文对象调用绑定到那个上下文对象
  4. 默认在在严格模式下绑定到undefined,否则绑定到全局对象

更安全地使用this 绑定的做法是传入一个特殊的对象,把this 绑定到这个对象。需要注意的是,箭头函数不使用this的4种规则,而是根据外层(函数或全局)作用域来决定this。

还要注意一点,eval 和 with 会导致作用域变化而引起性能下降,尽量不要使用。eval() 函数中的字符串是代码,用来执行动态创建的代码,严格模式有自己的作用域,还存在安全隐患;with 是重复引用一个对象中的多个属性的快捷方式,通过将一个对象的引用当作作用域来处理,会改变作用域范围。

处理和执行方式

JavaScript引擎本身没有时间概念,只是一个按需执行任意代码片段的环境,事件调度总是由包含它的宿主环境来执行。一旦有事件需要运行,事件循环队列就会运行,直到队列清空,用户交互、IO和定时器等事件源会向事件队列加入事件。

由于JavaScript的单线程特性,很多函数的代码具有原子性。

回调函数封装了程序的延续性,常见设计是分离回调(一个用于成功通知,一个用于出错通知)。另一种回调模式是“error-first”,可能受到防御式编程的影响,NodeJS API 采用了此类的风格,如果成功的话,这个参数就会被清空。需要注意的是,回调函数的嵌套往往称为回调地狱。

Deferred是一种将异步处理串联书写并执行的机制,Deferred对象是一种具有unresolved,resolved,rejected 中某一种状态的对象。Deferred内部机制是先注册回调函数,Deferred对象状态发生变化时执行该函数,是一种提高代码可读性的机制。

Deferred对象的状态迁移只能发生一次,以then(),done(),fail(),always(),pipe()指定后续函数的方法,通过when()来并行处理,将Deferred 对象中的一部分方法删除后得到是Promise对象,对状态的管理由最初创建该Deferred对象的所有者来执行。

Promise 封装了依赖于时间的状态,从而使得本身与时间无关,Promise 可以按照可预测的方式进行,而不用关心时序或底层的结果。一旦Promise决议完成,就成为了不变值,可以安全地吧这个值传递给第三方,并确保不会改变。

Promise 是一种在异步任务中作为两个或更多步骤的流程控制机制,时序上的this-then-that。 不仅表达了多步异步序列的流程控制,还是一个从一个步骤到下一个步骤传递消息的消息通道。事件监听对象可以当成是对promise 的一种模拟,对控制反转的恢复实现了更好的关注点分离。

判断是否是Promise 值的示例代码如下:

 
 
 
 
  1. if(
  2.     p !==null &&
  3.     ( typeof p ===“object” || typeof p ===“function”) && typeof p.then===“function”)
  4.     {
  5.         console.log(“thenable”);
  6.     }
  7. else{
  8.     console.log(“not thenable”);
  9. }

生成器是一类特殊的函数,可以一次或多次启动和停止,并不非的一定要完成,生成器把while true 带回了Javascript的世界。其中,yield 委托的主要目的是代码组织,以达到与普通函数调用的对称。从生成器yield出一个Promise, 并且让这个Promise 通过一个辅助函数恢复这个生成器,这是通过生成器管理异步的好方法之一。

需要注意的是,如果在Promise.all([..]) 中传入空数组,会立即完成, 而Promise.race([..]) 则会挂住。 在各种Promise库中,finally ( .. ) 还是会创建并返回一个新Promise的。

模块与库

模块和库是JavaScript 时空中的另一纽带,提高了代码的复用性和开发效率。

模块充分利用了闭包的强大能力,从模块中返回一个实际的对象并不是必须的,也可以直接返回一个内部函数,例如:jQauery 和 $标识符就是jQuery 模块的公共API。

模块有两个必要条件:

  1. 必须有外部的封闭函数,该函数必须至少被调用一次
  2. 封闭函数必须返回至少一个内部函数,这样内部函数才能在私有作用域中形成闭包,并且可以访问或修改私有的状态

import 可以将一个模块的一个或多个API导入到当前作用域中,并分别绑定在一个变量上;module 则将整个模块的API 导入并绑定到一个变量上, export 将当前模块的一个标识符导出为公共API。

大多数模块所依赖的加载器/管理器本质上是将这种模块定义封装进一个API。基于函数的模块并不是一个能被静态识别的模式(编译器),API定义只有在运行时考虑进来。但是ES6 模块的API 是静态的,必须被定义在独立的文件中。

JavaScript 中的库浩如烟海,这里仅对JQuery做简要说明。JQuery压缩后大约31k,轻巧灵活,通过链式语法实现逻辑功能,通过CSS3选择器及自定义选择器获取元素,支持插件,可扩展性高。

JQuery中 的特色函数——$ ,可以抽取与选择器匹配的元素,或者创建新的DOM元素,将已有的DOM元素转换为jQuery对象,对DOM构造完成后的事件监听器进行设定等等。JQuery 对DOM,样式,AJAX 均可有效处理。

通过扩展JQuery.fn 就可以创建JQuery的插件,code.google.com/apis/libraries 给出了很多JQuery 的插件信息。

利用JavaScript 的时空观,可以对这一语言有一些基本的梳理。就语言本身而言,关键字是不能回避的,对JavaScript 关键字,在StackOverFlow中有人给出了如下诗一样的总结:

 
 
 
 
  1. Let this long package float,
  2. Goto private class if short。
  3. While protected with debug case,
  4. Continue volatile interface。
  5. Instanceof super synchronized throw,
  6. Extends final export throws.
  7. Try import double enum?
  8. -False, boolean, abstract function.
  9. Implements typeof transient break!
  10. Void static,default do,
  11. Switch int native new,
  12. else, delete null public var,
  13. In return for const, true, char,
  14. …… finally catch byte.

客户端应用

一门语言所被使用的广泛程度取决于使用的场景,正如PHP被广泛采用那样,互联网应用不仅是JavaScript 的家乡,而且是它大展身手的最重要场所,没有JavaScript 的Web应用几乎绝迹了。

web应用中使用JavaScript有拖拽操作,异步读取,键盘访问 和动画效果等基本功能。对于清晰地使用JavaScript实现Web应用而言,理解浏览器网页处理过程是必要的。一般地,浏览器先分析HTML,然后构造DOM树,再载入外部Javascript 文件以及CSS文件,接下来载入图像文件等外部资源,最后在分析Javascript后开始执行至全部完成。

在HTML中加载JavaScript的方式有多种:

其他资讯

让你的专属顾问为你服务