事件委托技术能让你避免对特定的每一个节点添加事件监听器;相反,事件监听器是被添加到它们的父元素上。事件监听器会分析从子元素冒泡上来的事件,找到是哪一个子元素的事件。javascript
优势:html
this 永远指向函数运行时所在的对象,而不是函数被建立时所在的对象。前端
原型继承的基础是原型链查找。java
原型链查找基本概念:
每个函数 F 都有一个原型对象(prototype)F.prototype
每个函数均可以经过 new 关键字化身成为一个类构造函数,new F 会产生一个对象 O
在调用对象的某个属性或者方法,好比 http://O.xxx 的时候,会首先查找对象自身是否有这个方法或者属性,若是没找到就会去对象的构造函数的原型对象中查找(注意有两个定语),也就是查找 O 的构造函数 F 的原型对象 http://F.prototype.xxx
F.prototype 也是一个对象,查找 http://F.prototype.xxx 的时候会重复第 3 步的过程web
这里只是声明一个叫foo的function,直接用()执行这样是不成功的,想要变成IIFE就要把声明变成表达式,就能够当即执行了,能够这样(function foo(){})()或者(function foo(){}()),这就是用括号把定义强转成表达式,固然还有其余方法,关键就是声明不能够执行,表达式才能够执行。面试
undefined:未定义,在变量没有赋值的时候的值即为undefined。“缺乏值”,就是此处应该有一个值,可是尚未定义。
underclared:即为被污染的命名,访问没有被声明的变量,会抛出异常,终止执行。尝试访问一个undeclared的变量,浏览器会报错,JS执行会中断。
null:是一个空的对象引用。“没有对象”,即该处不该该有值ajax
区别:
undefined和null在if语句中,都会被自动转为false,相等运算符甚至直接报告二者相等。typeof undefined会返回undefined ,而typeof null 总返回 object(typeof有六种可能:“number”、“string”、“boolean”、“object”、“function”、“undefined”)算法
false == undefined;//false false == null;//false null == undefined;//true
该如何检测它们?typescript
var obj; obj ===undefined; //检测undfined 方法一 typeof obj === ‘undefined’;//检测undefined方法2 obj = null; obj === null;//来检测null typeof null;//‘object’
定义:闭包就是能够读取到其余函数内部变量的函数。
闭包的用途:express
注意:
匿名函数能够用做回调函数执行,能够防止全局变量污染。
在 JS 框架中常使用匿名函数来避免全局变量的污染。
$.(“input”).each(function(e){this.val(‘OK’)});
(function(){})();
$(document).ready(function(){ });
$(function() {})
原生对象:独立于宿主环境的 ECMAScript 实现提供的对象。为array obj regexp date function等能够new实例化的对象。
内置对象:为gload Math 等,开发者没必要明确实例化内置对象,它已被实例化了。相似于isNaN()、parseInt()和parseFloat()方法等,看起来都是函数,而实际上,它们都是Global对象的方法。具体能够参考 JavaScript 全局对象
宿主对象:即由 ECMAScript 实现的宿主环境(操做系统和浏览器)提供的对象。全部的BOM和DOM对象都是宿主对象。由于其对于不一样的“宿主”环境所展现的内容不一样(这就是兼容性和特性检测的原因)。ECMAScript官方未定义的对象都属于宿主对象。
第一个为函数声明,第二个将函数person()返回值赋值给person,第三个经过Person()的构造器建立了一个对象让person变量引用该对象;
call和apply都是调用一个对象的一个方法,以另外一个对象替换当前对象。它们都属于Function.prototype的一个方法,因此每一个function实例都有call和apply属性。这两个方法能够用来代替另外一个对象调用一个方法,可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。
区别:
二者传递的参数不一样,虽然函数第一个参数都是要传入给当前对象的对象,可是,apply的第二个参数是一个参数数组,将多个参数组合成为一个数组传入;而call第二个参数则是直接的参数列表。
Function.prototype.bind()其实就是函数绑定。函数的接收者取决于他是如何被调用,能够经过调用.bind()给函数绑定做用域上下文(this的值),即函数的接收者。
var foo = { x: 3} var bar = function(){console.log( this.x);} bar(); // undefinedvar boundFunc = bar.bind(foo);//隐式看做是在foo做用域里调用bar方法 boundFunc(); // 3
.bind()建立了一个函数,当这个函数在被调用的时候,它的 this 关键词会被设置成被传入的值(这里指调用bind()时传入的参数)也就是咱们传入想要的上下文。 简单的用法: 关于 Function.prototype.bind() 内部,这里有个很是简单的例子:
Function.prototype.bind = function (scope) { var fn = this; return function () { return fn.apply(scope);//使用call效果同样 }; }
document.write()方法能够用在两个方面:
记住,在载入页面后,浏览器输出流自动关闭。在此以后,任何一个对当前页面进行操做的document.write()方法将打开—个新的输出流,它将清除当前页面内容(包括源文档的任何变量或值)。所以,假如但愿用脚本生成的HTML替换当前页面,就必须把HTML内容链接起来赋给一个变量,使用一个document.write()方法完成写操做。没必要清除文档并打开一个新数据流,一个document.write()调用就可完成全部的操做。
关于document.write()方法还有一点要说明的是它的相关方法document.close()。脚本向窗口(无论是本窗口或其余窗口)写完内容后,必须关闭输出流。在延时脚本的最后一个document.write()方法后面,必须确保含有document.close()方法,不这样作就不能显示图像和表单。而且,任何后面调用的document.write()方法只会把内容追加到页面后,而不会清除现有内容来写入新值。为了演示document.write()方法,咱们提供了同一个应用程序的两个版本。
大多数生成的广告代码依旧使用 document.write(),虽然这种用法会让人很不爽。
检测浏览器的特殊名称和版本(用户代理检测)即浏览器UA字符串嗅探。浏览器嗅探技术能够快捷的将代码进行分支,以便针对不一样的浏览器应用不一样的指令;针对特定浏览器的特定版本,超出范围以外都是不可靠的
优点:能够刷新局部页面,而不用总体页面都刷新
缺点:用户禁用javascript的状况
工做原理:JSONP动态建立script标签,回调函数。Ajax是页面无刷新请求数据操做,动态添加一个<script>标签,而script标签的src属性是没有跨域的限制的。这样说来,这种跨域方式其实与ajax XmlHttpRequest协议无关了。
当GET请求从被调用页面返回时,能够返回一段JavaScript代码,这段代码会自动调用主页面中的一个callback函数。
优势:不受同源策略的影响,它的兼容性更好,在更加古老的浏览器中均可以运行,不须要XMLHttpRequest或ActiveX的支持;而且在请求完毕后能够经过调用callback的方式回传结果
缺点:只支持GET请求而不支持POST等其它类型的HTTP请求;它只支持跨域HTTP请求这种状况,不能解决不一样域的两个页面之间如何进行JavaScript调用的问题。
在JavaScript代码运行以前实际上是有一个编译阶段的。编译以后才是从上到下,一行一行解释执行。变量提高就发生在编译阶段,它把变量和函数的声明提高至做用域的顶端。(编译阶段的工做之一就是将变量与其做用域进行关联)。
变量提高须要注意两点:
函数声明:
从目标元素开始,往顶层元素传播。途中若是有节点绑定了相应的事件处理函数,这些函数都会被依次触发。若是想阻止事件起泡,可使用e.stopPropagation()(Firefox)或者e.cancelBubble=true(IE)来组织事件的冒泡传播
DOM元素的attribute和property二者是不一样的东西。attribute翻译为“特性”,property翻译为“属性”。
attribute是一个特性节点,每一个DOM元素都有一个对应的attributes属性来存放全部的attribute节点,attributes是一个类数组的容器,说得准确点就是NameNodeMap,不继承于Array.prototype,不能直接调用Array的方法。attributes的每一个数字索引以名值对(name=”value”)的形式存放了一个attribute节点。
property就是一个属性,若是把DOM元素当作是一个普通的Object对象,那么property就是一个以名值对(name=”value”)的形式存放在Object中的属性。要添加和删除property和普通的对象相似。
不少attribute节点还有一个相对应的property属性,好比上面的div元素的id和class既是attribute,也有对应的property,无论使用哪一种方法均可以访问和修改。
总之,attribute节点都是在HTML代码中可见的,而property只是一个普通的名值对属性
由于你不知道哪一天浏览器或javascript自己就会实现这个方法,并且和你扩展的实现有不一致的表现。到时候你的javascript代码可能已经在无数个页面中执行了数年,而浏览器的实现致使全部使用扩展原型的代码都崩溃了。
须要给Array原型添加一个distinct的方法,最好检查是否存在同名的方法,避免自定义方法覆盖原生方法:
Arrray.prototype.distinct = Arrray.prototype.distinct || function(){/…../}
ready 表示文档的 DOM 已经加载完成(不包含图片、视频等资源);load 表示整个网页加载完成。能够看出,ready 事件发生在 load 事件以前。
若是两边的操做数具备一致的类型且拥有相同的值时,=== 返回 true,!== 返回 false。
同源策略限制了一个源(origin)中加载文本或脚本与来自其它源(origin)中资源的交互方式。
同源策略出于安全,不容许源 A 的脚本读取(read)源 B 的资源的内容,但却容许执行(execute)源 B 的资源。这个概念也有些拗口。简单说,有一个页面调用了 Google CDN 提供的 jQuery,以及其它 CDN 上的 Bootstrap JS、CSS 代码,虽然它们与个人博客不一样源,但我能够用它们来操做这个页面,并应用样式,这是执行的概念。
将此方法添加至 Array.prototype 实现,代码以下:
Array.prototype.duplicator = function(){ var l = this.length,i; for(i=0;i<l;i++){ this.push(this[i]) } }
一个运算符若是有一个操做数,为一元运算符,两个为二元,三个为三元运算符,三元表达式则为一个三元运算表达式!
ECMAScript5中引入的严格模式,经过让JavaScript运行环境对一些开发过程当中最多见和不易发现的错误作出和当前不一样的处理,来让开发者拥有一个”更好”的JavaScript语言。
好处:
好处具体体现:
坏处:一样的代码,在“严格模式”中,可能会有不同的运行结果;一些在“正常模式”下能够运行的语句,在“严格模式”下将不能运行
总结:启用JavaScript严格模式,它能帮你发现代码中不曾注意到的错误。不要在全局环境中启用,但你能尽可能多的使用IIFE(当即执行函数表达式)来把严格模式做用到多个函数范围内。一开始,你会遇到以前不曾碰到过的错误提示,这是正常的。当启用严格模式后,请确保在支持的浏览器中作了测试,以发现新的潜在问题。必定不要仅仅在代码中添加一行”use strict”就假定余下的代码能正常工做。
for (var i = 1; i <= 30; i++) { if (i % 3 === 0) { if (i % 5 === 0) { alert('fizzbuzz' + i); continue; } alert('fizz' + i); continue; } else if (i % 5 === 0) { if (i % 3 === 0) { alert('fizzbuzz' + i); continue; } alert('buzz' + i); continue; } }
它的意思是: 尽可能少在全局做用域定义变量。
目的:减小名称冲突 利于模块化
要等到等页面彻底加载后(全部图像、javascript文件、CSS等外部文件)。替代:把script标签放到最后面。
单页应用是一种特殊的web应用,它将全部的活动局限于一个web页面中,仅在该Web页面初始化时加载相应的HTML、JavaScript 和 CSS。
优势:
缺点:
优势:易读性改善
以Typescript为例子:
typescript是javascript的强类型版本,在编译期去掉类型和特有语法,生成纯粹的javascript代码。TypeScript 是 JavaScript 的超集,这意味着他支持全部的 JavaScript 语法。并在此之上对 JavaScript 添加了一些扩展,如 class / interface / module 等。这样会大大提高代码的可阅读性。
优势:
缺点:
javascript中的原始值(undefined、null、布尔值、数字和字符串)与对象(包括数组和函数)有着根本区别。原始值是不可更改的:任何方法都没法更改(或“突变”)一个原始值。对数字和布尔值来讲显然如此—-改变数字的值自己就说不通,而对字符串来讲就不那么明显了,由于字符串看起来像由字符组成的数组,咱们指望能够经过指定索引来假改字符串中的字符。实际上,javascript是禁止这样作的。字符串中全部的方法看上去返回了一个修改后的字符串,实际上返回的是一个新的字符串值。
区别:
优势:
*由于不能修改一个不变对象的状态,因此能够避免由此引发的没必要要的程序错误;一个不变的对象要比一个可变的对象更加容易维护。
缺点:
可使用const 修饰变量不可变
同步式:当计算机调度线程进行I/O操做命令后,因为文件的读写或者网络通讯须要较长的操做时间,操做系统为了充分利用cpu,此时会暂停到当前的I/O线程对CPU的控制(故又称同步式为阻塞式I/O),把cup资源然给其余的线程资源,当I/O线程完成了操做时,此时操做系统会恢复此时的I/O线程,从而当前I/O线程从新得到了cup的的控制权,继续完成其余操做。
异步式:异步式IO又称非阻塞式I/O,异步式与同步式不一样的是,当线程进行IO操做时,操做系统并非暂停当前的线程操做,而是执行完I/O指令后,操做系统继续让当前线程执行下一条指令,当I/O操做完成后,会经过事件(event)通知I/O线程,而线程在接收到通知后,会处理响应事件。
主线程从“任务队列”中读取事件,这个过程是循环不断的,因此整个的这种运行机制又称为Event Loop(事件循环)。
所谓“回调函数”(callback),就是那些会被主线程挂起来的代码。异步任务必须指定回调函数,当异步任务从“任务队列”回到执行栈,回调函数就会执行。
“任务队列”是一个先进先出的数据结构,排在前面的事件,优先返回主线程。主线程的读取过程基本上是自动的,只要执行栈一清空,“任务队列”上第一位的事件就自动返回主线程。可是,因为存在后文提到的“定时器”功能,主线程要检查一下执行时间,某些事件必需要在规定的时间返回主线程。
第一个未函数声明,第二个为函数定义表达式(函数定义表达式foo,变量声明提早单赋值并未提早)
(function(){ var a = b = 3; })(); console.log("a defined? " + (typeof a !== 'undefined')); console.log("b defined? " + (typeof b !== 'undefined'));
输出: a defined? false b defined? true
这是一个愈来愈广泛的作法,被许多流行的JavaScript库(jQuery,Node.js等)采用。这种技术建立了一个围绕文件所有内容的闭包,也许是最重要的是,建立了一个私有的命名空间,从而有助于避免不一样JavaScript模块和库之间潜在的名称冲突。
这种技术的另外一个特色是,容许一个易于引用的(假设更短的)别名用于全局变量。这一般用于,例如,jQuery插件中。jQuery容许你使用jQuery.noConflict(),来禁用 $ 引用到jQuery命名空间。在完成这项工做以后,你的代码仍然可使用$ 利用这种闭包技术,以下所示:
(function($) { /* jQuery plugin code referencing $ */ } )(jQuery);
use strict 是一种在JavaScript代码运行时自动实行更严格解析和错误处理的方法。那些被忽略或默默失败了的代码错误,会产生错误或抛出异常。一般而言,这是一个很好的作法。
严格模式的一些主要优势包括:
function foo1(){ return { bar: "hello" }; }function foo2(){ return { bar: "hello" }; }
返回不相同: foo1 returns:Object {bar: "hello"}foo2 returns:undefined
分号在JavaScript中是一个可选项(尽管省略它们一般是很是糟糕的形式)。其结果就是,当碰到 foo2()中包含 return语句的代码行(代码行上没有其余任何代码),分号会当即自动插入到返回语句以后。也不会抛出错误,由于代码的其他部分是彻底有效的,即便它没有获得调用或作任何事情(至关于它就是是一个未使用的代码块,定义了等同于字符串 "hello"的属性 bar)。
这种行为也支持放置左括号于JavaScript代码行的末尾,而不是新代码行开头的约定。正如这里所示,这不只仅只是JavaScript中的一个风格偏好。
NaN 属性表明一个“不是数字”的值。这个特殊的值是由于运算不能执行而致使的,不能执行的缘由要么是由于其中的运算对象之一非数字(例如, "abc" / 4),要么是由于运算的结果非数字(例如,除数为零)。
NaN的特色:
测试数字为NaN的方法:
console.log(0.1 + 0.2);console.log(0.1 + 0.2 == 0.3);
JavaScript中的数字和浮点精度的处理相同,所以,可能不会老是产生预期的结果。
以上所提供的例子就是一个演示了这个问题的典型例子。但出人意料的是,它会输出:
0.30000000000000004false
ECMAScript 6 以前没有提供相似 Number.isInteger 的方法。在ECMAScript规格说明中,整数只概念上存在:即,数字值老是存储为浮点值。
方法:
虽然这个以 parseInt函数为基础的方法在 x 取许多值时都能工做良好,但一旦 x 取值至关大的时候,就会没法正常工做。问题在于 parseInt() 在解析数字以前强制其第一个参数到字符串。所以,一旦数目变得足够大,它的字符串就会表达为指数形式(例如, 1e+21)。所以,parseInt() 函数就会去解析 1e+21,但当到达 e字符串的时候,就会中止解析,所以只会返回值 1。注意:
String(1000000000000000000000)'1e+21'> parseInt(1000000000000000000000, 10)1> parseInt(1000000000000000000000, 10) === 1000000000000000000000false
(function() { console.log(1); setTimeout(function(){console.log(2)}, 1000); setTimeout(function(){console.log(3)}, 0); console.log(4); })();
序号以下:
1 4 3 2
比较明显而易见的那部分:
1 和 4之因此放在前面,是由于它们是经过简单调用 console.log() 而没有任何延迟输出的
2 之因此放在 3的后面,是由于 2 是延迟了1000毫秒(即,1秒)以后输出的,而 3 是延迟了0毫秒以后输出的。
好的。可是,既然 3 是0毫秒延迟以后输出的,那么是否意味着它是当即输出的呢?若是是的话,那么它是否是应该在 4 以前输出,既然 4 是在第二行输出的?
要回答这个问题,你须要正确理解JavaScript的事件和时间设置。
浏览器有一个事件循环,会检查事件队列和处理未完成的事件。例如,若是时间发生在后台(例如,脚本的 onload 事件)时,浏览器正忙(例如,处理一个 onclick),那么事件会添加到队列中。当onclick处理程序完成后,检查队列,而后处理该事件(例如,执行 onload 脚本)。
一样的, setTimeout() 也会把其引用的函数的执行放到事件队列中,若是浏览器正忙的话。
当setTimeout()的第二个参数为0的时候,它的意思是“尽快”执行指定的函数。具体而言,函数的执行会放置在事件队列的下一个计时器开始。可是请注意,这不是当即执行:函数不会被执行除非下一个计时器开始。这就是为何在上述的例子中,调用 console.log(4) 发生在调用 console.log(3) 以前(由于调用 console.log(3) 是经过setTimeout被调用的,所以会稍微延迟)。
下面这个函数在 str 是回文结构的时候返回true,不然,返回false。
function isPalindrome(str) { str = str.replace(/\W/g, '').toLowerCase(); return (str == str.split('').reverse().join('')); }
例如:
console.log(isPalindrome("level")); // logs 'true'console.log(isPalindrome("levels")); // logs 'false'console.log(isPalindrome("A car, a man, a maraca")); // logs 'true'
console.log(sum(2,3)); // Outputs 5 console.log(sum(2)(3)); // Outputs 5
(至少)有两种方法能够作到:
方法1:
function sum(x) { if (arguments.length == 2) { return arguments[0] + arguments[1]; }else { return function(y) { return x + y; }; } }
方法2:
function sum(x, y) { if (y !== undefined) { return x + y; } else { return function(y) { return x + y; }; } }
for (var i = 0; i < 5; i++) { var btn = document.createElement('button'); btn.appendChild(document.createTextNode('Button ' + i)); btn.addEventListener('click', function(){ console.log(i); }); document.body.appendChild(btn); }
(a)当用户点击“Button 4”的时候会输出什么到控制台,为何?
不管用户点击什么按钮,数字5将总会输出到控制台。这是由于,当 onclick 方法被调用(对于任何按钮)的时候, for 循环已经结束,变量 i 已经得到了5的值。
(b)提供一个或多个备用的可按预期工做的实现方案?
要让代码工做的关键是,经过传递到一个新建立的函数对象,在每次传递经过 for 循环时,捕捉到 i 值。下面是三种可能实现的方法:
方法一
for (var i = 0; i < 5; i++) {
var btn = document.createElement('button'); btn.appendChild(document.createTextNode('Button ' + i)); btn.addEventListener('click', (function(i) { return function() { console.log(i); }; })(i)); document.body.appendChild(btn);
}
方法二: 封装所有调用到在新匿名函数中的 btn.addEventListener
for (var i = 0; i < 5; i++) {
var btn = document.createElement('button'); btn.appendChild(document.createTextNode('Button ' + i)); (function (i) { btn.addEventListener('click', function() { console.log(i); }); })(i); document.body.appendChild(btn);
}
方法三: 调用数组对象的本地 forEach 方法来替代 for 循环
['a', 'b', 'c', 'd', 'e'].forEach(function (value, i) {
var btn = document.createElement('button'); btn.appendChild(document.createTextNode('Button ' + i)); btn.addEventListener('click', function() { console.log(i); }); document.body.appendChild(btn);
});
var arr1 = "john".split(''); var arr2 = arr1.reverse(); var arr3 = "jones".split(''); arr2.push(arr3); console.log("array 1: length=" + arr1.length + " last=" + arr1.slice(-1)); console.log("array 2: length=" + arr2.length + " last=" + arr2.slice(-1));
输出结果是:
"array 1: length=5 last=j,o,n,e,s""array 2: length=5 last=j,o,n,e,s"
调用数组对象的 reverse() 方法并不仅返回反顺序的阵列,它也反转了数组自己的顺序(即,在这种状况下,指的是 arr1)。
reverse() 方法返回一个到数组自己的引用(在这种状况下即,arr1)。其结果为,arr2 仅仅是一个到 arr1的引用(而不是副本)。所以,当对 arr2作了任何事情(即当咱们调用 arr2.push(arr3);)时,arr1 也会受到影响,由于 arr1 和 arr2 引用的是同一个对象。
注意:
传递数组到另外一个数组的 push() 方法会让整个数组做为单个元素映射到数组的末端。其结果是,语句 arr2.push(arr3); 在其总体中添加 arr3 做为一个单一的元素到 arr2 的末端(也就是说,它并无链接两个数组,链接数组是 concat() 方法的目的)。
和Python同样,JavaScript标榜数组方法调用中的负数下标,例如 slice() 可做为引用数组末尾元素的方法:例如,-1下标表示数组中的最后一个元素,等等。
console.log(1 + "2" + "2"); console.log(1 + +"2" + "2"); console.log(1 + -"1" + "2"); console.log(+"1" + "1" + "2"); console.log( "A" - "B" + "2"); console.log( "A" - "B" + 2);
上面的代码将输出如下内容到控制台:
"122""32""02""112""NaN2"NaN
根本缘由是,JavaScript(ECMAScript)是一种弱类型语言,它可对值进行自动类型转换,以适应正在执行的操做。让咱们经过上面的例子来讲明这是如何作到的。
例1:1 + "2" + "2" 输出:"122" 说明: 1 + "2" 是执行的第一个操做。因为其中一个运算对象("2")是字符串,JavaScript会假设它须要执行字符串链接,所以,会将 1 的类型转换为 "1", 1 + "2"结果就是 "12"。而后, "12" + "2" 就是 "122"。
例2: 1 + +"2" + "2" 输出: "32" 说明:根据运算的顺序,要执行的第一个运算是 +"2"(第一个 "2" 前面的额外 + 被视为一元运算符)。所以,JavaScript将 "2" 的类型转换为数字,而后应用一元 + 号(即,将其视为一个正数)。其结果是,接下来的运算就是 1 + 2 ,这固然是 3。而后咱们须要在一个数字和一个字符串之间进行运算(即, 3 和 "2"),一样的,JavaScript会将数值类型转换为字符串,并执行字符串的链接,产生 "32"。
例3: 1 + -"1" + "2" 输出: "02" 说明:这里的解释和前一个例子相同,除了此处的一元运算符是 - 而不是 +。先是 "1" 变为 1,而后当应用 - 时又变为了 -1 ,而后将其与 1相加,结果为 0,再将其转换为字符串,链接最后的 "2" 运算对象,获得 "02"。
例4: +"1" + "1" + "2" 输出: "112" 说明:虽然第一个运算对象 "1"由于前缀的一元 + 运算符类型转换为数值,但又当即转换回字符串,当链接到第二个运算对象 "1" 的时候,而后又和最后的运算对象"2" 链接,产生了字符串 "112"。
例5: "A" - "B" + "2" 输出: "NaN2" 说明:因为运算符 - 不能被应用于字符串,而且 "A" 和 "B" 都不能转换成数值,所以,"A" - "B"的结果是 NaN,而后再和字符串 "2" 链接,获得 "NaN2" 。
例6: "A" - "B" + 2 输出: NaN 说明:参见前一个例子, "A" - "B" 结果为 NaN。可是,应用任何运算符到NaN与其余任何的数字运算对象,结果仍然是 NaN。
var list = readHugeList(); var nextListItem = function() { var item = list.pop(); if (item) { // process the list item... nextListItem(); } };
潜在的堆栈溢出能够经过修改nextListItem 函数避免:
var list = readHugeList(); var nextListItem = function() { var item = list.pop(); if (item) { // process the list item... setTimeout( nextListItem, 0); } };
堆栈溢出之因此会被消除,是由于事件循环操纵了递归,而不是调用堆栈。当 nextListItem 运行时,若是 item不为空,timeout函数(nextListItem)就会被推到事件队列,该函数退出,所以就清空调用堆栈。当事件队列运行其timeout事件,且进行到下一个 item 时,定时器被设置为再次调用 extListItem。所以,该方法从头至尾都没有直接的递归调用,因此不管迭代次数的多少,调用堆栈保持清空的状态。
闭包是一个能够访问外部(封闭)函数做用域链中的变量的内部函数。
闭包能够访问三种范围中的变量:这三个范围具体为:
下面是一个简单的例子:
var globalVar = "xyz"; (function outerFunc(outerArg) { var outerVar = 'a'; (function innerFunc(innerArg) { var innerVar = 'b'; console.log( "outerArg = " + outerArg + "\n" + "innerArg = " + innerArg + "\n" + "outerVar = " + outerVar + "\n" + "innerVar = " + innerVar + "\n" + "globalVar = " + globalVar); })(456); })(123);
在上面的例子中,来自于 innerFunc, outerFunc和全局命名空间的变量都在 innerFunc的范围内。所以,上面的代码将输出以下:
outerArg = 123innerArg = 456outerVar = ainnerVar = bglobalVar = xyz
for (var i = 0; i < 5; i++) { setTimeout(function() { console.log(i); }, i * 1000 ); }
解释你的答案。闭包在这里能起什么做用?
上面的代码不会按预期显示值0,1,2,3,和4,而是会显示5,5,5,5,和5。
缘由是在循环中执行的每一个函数将整个循环完成以后被执行,所以,将会引用存储在 i中的最后一个值,那就是5。
闭包能够经过为每次迭代建立一个惟一的范围,存储范围内变量的每一个惟一的值,来防止这个问题,以下:
for (var i = 0; i < 5; i++) { (function(x) { setTimeout(function() { console.log(x); }, x * 1000 ); })(i); }
这就会按预期输出0,1,2,3,和4到控制台。
console.log("0 || 1 = "+(0 || 1)); console.log("1 || 2 = "+(1 || 2)); console.log("0 && 1 = "+(0 && 1)); console.log("1 && 2 = "+(1 && 2));
该代码将输出:
0 || 1 = 11 || 2 = 10 && 1 = 01 && 2 = 2
在JavaScript中, || 和 &&都是逻辑运算符,用于在从左至右计算时,返回第一个可彻底肯定的“逻辑值”。
或( || )运算符。在形如 X||Y的表达式中,首先计算X 并将其解释执行为一个布尔值。若是这个布尔值true,那么返回true(1),再也不计算 Y,由于“或”的条件已经知足。若是这个布尔值为false,那么咱们仍然不能知道 X||Y是真是假,直到咱们计算 Y,而且也把它解释执行为一个布尔值。
所以, 0 || 1 的计算结果为true(1),同理计算1 || 2。
与( &&)运算符。在形如 X&&Y的表达式中,首先计算 X并将其解释执行为一个布尔值。若是这个布尔值为 false,那么返回 false(0),再也不计算 Y,由于“与”的条件已经失败。若是这个布尔值为true,可是,咱们仍然不知道 X&&Y 是真是假,直到咱们去计算 Y,而且也把它解释执行为一个布尔值。
不过,关于 &&运算符有趣的地方在于,当一个表达式计算为“true”的时候,那么就返回表达式自己。这很好,虽然它在逻辑表达式方面计算为“真”,但若是你但愿的话也可用于返回该值。这就解释了为何,有些使人奇怪的是, 1 && 2返回 2(而不是你觉得的可能返回 true 或 1)。
console.log(false == '0') console.log(false === '0')
代码将输出:
true false
在JavaScript中,有两种等式运算符。三个等于运算符 === 的做用相似传统的等于运算符:若是两侧的表达式有着相同的类型和相同的值,那么计算结果为true。而双等于运算符,会只强制比较它们的值。所以,整体上而言,使用 ===而不是 ==的作法更好。 !==vs !=亦是同理。
var a={}, b={key:'b'}, c={key:'c'}; a[b]=123; a[c]=456; console.log(a[b]);
这段代码将输出 456(而不是 123)
缘由为:当设置对象属性时,JavaScript会暗中字符串化参数值。在这种状况下,因为 b 和 c都是对象,所以它们都将被转换为"[object Object]"。结果就是, a[b]和a[c]均至关于a["[object Object]"] ,并能够互换使用。所以,设置或引用 a[c]和设置或引用 a[b]彻底相同。
console.log((function f(n){return ((n > 1) ? n * f(n-1) : n)})(10));
代码将输出10!的值(即10!或3628800)。
缘由是:
命名函数 f()递归地调用自己,当调用 f(1)的时候,只简单地返回1。下面就是它的调用过程:
f(1): returns n, which is 1f(2): returns 2 f(1), which is 2f(3): returns 3 f(2), which is 6f(4): returns 4 f(3), which is 24f(5): returns 5 f(4), which is 120f(6): returns 6 f(5), which is 720f(7): returns 7 f(6), which is 5040f(8): returns 8 f(7), which is 40320f(9): returns 9 f(8), which is 362880f(10): returns 10 * f(9), which is 3628800
(function(x) { return (function(y) { console.log(x); })(2) })(1);
控制台将输出 1,即便历来没有在函数内部设置过x的值。缘由是:
闭包是一个函数,连同在闭包建立的时候,其范围内的全部变量或函数一块儿。在JavaScript中,闭包是做为一个“内部函数”实施的:即,另外一个函数主体内定义的函数。闭包的一个重要特征是,内部函数仍然有权访问外部函数的变量。
所以,在本例中,因为 x未在函数内部中定义,所以在外部函数范围中搜索定义的变量 x,且被发现具备1的值。
var hero = { _name: 'John Doe', getSecretIdentity: function (){ return this._name; } }; var stoleSecretIdentity = hero.getSecretIdentity; console.log(stoleSecretIdentity()); console.log(hero.getSecretIdentity());
代码将输出:
undefinedJohn Doe
第一个 console.log之因此输出 undefined,是由于咱们正在从 hero对象提取方法,因此调用了全局上下文中(即窗口对象)的 stoleSecretIdentity(),而在此全局上下文中, _name属性不存在。
其中一种修复stoleSecretIdentity() 函数的方法以下:
var stoleSecretIdentity = hero.getSecretIdentity.bind(hero);
此函数的参数为:
DOM元素
回调函数(将DOM元素做为其参数)
访问树(DOM)的全部元素是经典的深度优先搜索算法应用。下面是一个示范的解决方案:
function Traverse(p_element,p_callback) { p_callback(p_element); var list = p_element.children; for (var i = 0; i < list.length; i++) { Traverse(list[i],p_callback); // recursive call } }