单元测试框架的核心是断言方法,一般叫assert()。
该方法一般接收一个值--须要断言的值,以及一个表示该断言目的的描述。
若是该值执行的结果为true,断言就会经过;
不然,断言就会被认为是失败的。
一般用一个相应的经过(pass)/ 失败(fail)标记记录相关的信息;javascript
function assert(value, desc) { let li = document.createElement('li'); li.className = value ? 'pass' : 'fail'; li.appendChild(document.createTextNode(desc)); document.getElementById('results').appendChild(li); } // 断言函数 function assert(value, desc) { if (value) { console.log(`\033[32m ${desc} \033[0m`); // 断言经过 绿色字体 } else { console.log(`\033[31m ${desc} \033[0m`); // 断言失败 红色字体 } }
JavaScript是一门函数式语言
java
在JavaScript中,函数是第一型对象。函数能够共处,能够视做为其余任意类型的对象。就像普通的JavaScript数据类型,,函数能够被任意变量进行引用,或声明成对象字面量,甚至能够将其做为函数参数进行传递。
跨域
函数是第一型对象
命名一个函数时,该名称在整个函数声明范围内是有效的。若是函数声明在顶层,window对象上的同名属性则会引用到该函数。
数组
全部的函数都有一个name属性,该属性保存的是该函数名称的字符串。匿名函数的name属性值为空。
浏览器
在JavaScript中,做用域是由function进行声明的,而不是代码块。声明的做用域建立于代码块,但不是终结于代码块(其余语言是终结于代码块的)
闭包
if (window) { var x = 123; } alert(x); 执行代码后,会弹出123,是由于JavaScript在大括号关闭处并无终止其做用域。
变量声明的做用域开始于声明的地方,结束于函数的结尾,与代码嵌套无关。
app
命名函数的做用域是指声明该函数的整个函数范围,与代码嵌套无关;
框架
对于做用域声明,全局上下文就像一个包含页面全部代码的超大型函数。
函数
全部的函数调用都会传递两个隐式参数:argument和this
单元测试
若是一个数不是做为方法、构造器、或者经过apply()或call()进行调用的,则认为它是“做为函数”进行调用的。
function ninja() {}; ninja() var samurai = function() {}; samurai()
以这种方式调用时,函数的上下文是全局上下文---window对象。
当一个函数被赋值给对象的一个属性,并使用引用该函数的这个属性进行调用时,那么函数就是做为该对象的一个方法进行调用的。
var 0 = {}; o.whatever = function() {}; o.whatever();
将函数做为对象的一个方法进行调用时,该对象就变成了函数上下文,而且在函数内部能够以this参数的形式进行访问。
将函数做为构造器进行调用,须要在函数调用前使用new关键字
function Ninja() { this.skulk = function() { return this; } } var ninja1 = new Ninja(); var ninja2 = new Ninja();
构造器的目的是经过函数调用初始化建立新的对象。
函数调用方式之间点主要差别是:做为this参数传递给执行函数的上下文对象之间点区别。
经过函数的apply()方法来调用函数,须要给apply()传入两个参数:一个是函数上下文的对象,另外一个是做为函数参数所组成的数组;
经过函数的call()方法来调用函数,须要给call()传入两个参数:一个是函数上下文的对象,另外一个是做为函数参数的参数列表,而不是单个数组;
function juggle() { var result = 0; for (var n = 0; n < arguments.length; n++) { result += arguments[n] } this.result = result; } var ninja1 = {}; var ninja2 = {}; juggle.apply(ninja1, [1,2,3,4]); juggle.call(ninja2, 5,6,7,8) assert(ninja1.result === 10, 'juggled via apply'); assert(ninja2.result === 26, 'juggled via call');
使用apply()和call()能够选择任意对象做为函数上下文;
为了避免让没必要要的函数名称污染全局命名空间,能够建立大量的小型函数进行传递,而不是建立包含大量命令语句的大型函数。
递归:当函数调用自身,或调用另一个函数,但这个函数的调用树中的某个地方又调用到了本身时,就产生了递归。
递归的两个条件:引用自身,而且有终止条件。
闭包是一个函数在建立时容许自身函数访问并操做该自身函数以外的变量时所建立的做用域
闭包可让函数访问全部的变量和函数,只要这些变量和函数存在于该函数声明时的做用域内就行。
var outerValue = 'ninja'; var later; function outerFunction() { // 该变量的做用域限制在该函数内部,而且在函数外部访问不到; var innerValue = 'samurai'; // 在外部函数内,声明一个内部函数。 // 注意:声明该函数时,innerValue是在做用域内的 function innerFunction() { assert(outerValue, 'I can see the ninja'); assert(innerValue, 'I can see the samurai'); // 将内部函数引用到later变量上,因为later在全局做用域内,因此能够对它进行调用。 later = innerFunction; } } // 调用外部函数,将会声明内部函数,并将内部函数赋值给later变量。 outerFunction(); // 经过later调用内部函数。 // 咱们不能直接调用内部函数,由于它的做用域(和innerValue一块儿)被限制在outerFunction内。 later();
在构造器内隐藏变量,使其在外部做用域不可访问,可是能够存在于闭包内。
function Ninja() { var feints = 0; this.getFenits = function() { return feints; } this.feint = function() { feints++; } } var ninja = new Ninja(); ninja.feint(); assert(ninja.getFenits() === 1, '调用一次,内部变量++'); assert(ninja.feints === undefined, '函数外部不可访问')
变量的做用域依赖于变量所在的闭包
闭包记住的是变量的引用,而不是闭包建立时刻该变量的值
全部的函数在初始化时都有一个prototype属性,该属性的初始值是一个空对象。
使用new操做符将函数做为构造器进行调用的时候,其上下文被定义为新对象的实例。
在构造器内的绑定操做优先级永远高于在原型上的绑定操做优先级。由于构造器的this上下文指向的是实例自身,因此咱们能够在构造器内对核心内容执行初始化操做。
经过instanceof操做,能够判断函数是否继承了其原型链中任何对象的功能。