开发小组内培训,自己编写的资料。现在没事发上来。 内容来源于从网上看到的一些文章。 主要介绍一些 javascript 比较混乱的语法和一些不太常用的用法。 基本语法 Js 是弱类型动态语言,所以任意变量名都可随时改变数据类型,在js 里面一切都是对象,一切都是数据。 函数是js 的一等公民。 Ojbect 是所有对象的父,遍历prototype 对象时最终一定会到达Object.prototype 变量的声明 • JS 的作用域只有全局和局部 • 所有运行于浏览器的js 解释器的全局作用域都是window ,其它的脱离浏览器的js 解释器都有各自的全局对象名称 • 所有全局变量都是全局对象的属性 • 函数也是一种数据类型,所以全局作用域的函数也是全局对象的属性 • var 关键字是指在当前作用域声明变量 Js代码 收藏代码 //eg. Test.js var a;// 在全局作用域声明一个变量 function f(){ var a;// 在函数f 作用域声明一个变量 } • 使用一个变量时如果没有在当前作用域链中找到就会在全局作用域查找。如果这个全局变量存在就使用,如果不存在就认为是undefined 。如果当前是赋值语句就自动创建一个全局变量 Js代码 收藏代码 //eg. Test.js a;// 在全局作用域声明一个全局变量 function f(){ a=1;// 在函数f 为全局变量a 赋值 b;//undefined c= ‘c ’;// 在函数f 内声明一个全局变量 } • 使用function fname (){} ,如果当前作用域是全局的,就声明一个全局函数 如果当前作用域是在函数内容,就声明一个内部函数 与var fname=function(){}区别如下 Js代码 收藏代码 function f1(){ f1();//出错 f2();//调用成功 var f1=function(){};//看过的一本老外的书上推荐这么写 function f2(){}//不推荐这么写 f1();//可调用成功 f2();//可调用成功 } • 由于任意类型都是数据,函数也不例外,可以把一个函数的定义作为变量值赋给一个变量 Js代码 收藏代码 function global(){// 在全局作用域声明一个全局函数 function global2(){}// 在函数内声明一个内部函数 var fn=function(){}; // 在函数内声明一个内部函数,分号不能丢 var fn2=function fn3(){ fn3();// 成功 }; fn3();//undefined } • Js 解析器会自动提升变量和函数声明到当前作用域的最前面,但是相应的赋值语句位置不变 • Js 变量只有函数作用域和全局作用域,没有块作用域。也就是在if for switch while 内声明的变量也是在整个函数内可见的 Js代码 收藏代码 alert(a);//undefined var f=function(){alert(a);}; var a=1; f();// 可访问 变量a 上面的代码相当于 Js代码 收藏代码 var a; alert(a);//undefined var f=function(){alert(a);}; a=1; f();// 可访问 变量a // 另一个例子 function f(){ for(var i=0;i<10;i++){var a=0;} alert(i+ ‘ ’+a);//10 0 } function f(){ for(var i=0;i<10;i++){ setTimeout(function(){alert(i);},500); // 一共alert10 个10 } }// 应该是下面的样子 function f(){ for(var i=0;i<10;i++){ setTimeout((function(i){ return function(){alert(i);}; })(i),500); } } // 或者是 function f(){ for(var i=0;i<10;i++){ (function(i){ setTimeout(function(){alert(i);},500); })(i); } } 变量的作用域链 • Js 有两个变量作用域链 • 一个是闭包上下文作用链 • 一个是prototype 继承作用域链 • 一个Function 对象如果是另一个对象的成员这个Function 对象就是它的拥有者的方法,如果一个Function 对象是独立的它就是一个函数 • 使用this.xxx的方式访问的变量,会首先在当前对象的prototype 查找变量,如果找不到就从它的父的prototype 查找,如果一直找不到会一直追溯到Object.prototype • Object.prorotype===null;//true • 不用this.xxx访问的变量,会首先在当前函数作用域查找变量,如果找不到会判断当前函数是不是闭包,如果不是就查找全局作用域。如果是闭包,就在创建闭包的函数内查找,直到全局作用域。 this 的引用 • this 一定会有一个引用它不可能是 undefined 或 null • 如果使用 this 的 Function 对象是一个函数,不论这个函数是不是闭包, this 一定会引用全局对象。 • 在一个对象成员方法内通过 this 获取属性会查找 prototype 继承链 • 在构造器内, this 引用新创建的对象 Js代码 收藏代码 function con(){ this.a=1; // 创建一个新属性, this 引用 new con(); } con.prototype={f:function(){alert(this.b);},b:2}; new con().f();//2 call 和 apply • 它们会临时改变一个函数的 this 引用 • this 引用 call 和 apply 的第一个参数 Js代码 收藏代码 function foo(arg){ alert(this.a); alert(arg); } var o={a:1}; foo.call(o,2);//1 2 foo.apply(o,[2]);//1 2 new 的用法 • 使用 new 调用一个函数时,这个函数默认返回新创建的对象 Js代码 收藏代码 function f(){} var o=new f();//o 的值实际是 new f 的返回值 等价于 var o=new f; function f1(){return 1;} var o=new f1();//(o===1)===true • 不使用 new 调用一个函数,函数默认返回 undefined Js代码 收藏代码 function f(){} var a=f();//undefined prototype • Js 采用 prototype 继承方式 • Object.prototype===null;//true • 一个构造器创造的所有实例都会引用同样的 prototype 对象,而不会为每一个新对象创建一个新 prototype 对象。 Js代码 收藏代码 function Asian(){}; Asian.prototype={haircolor: ’ black ’ }; new Asian().constructor.prototype.haircolor= ‘ white ’ ; new Asian().haicolor;// ’ white ’ • new Asian().haircolor='white'; 这种写法,实际是直接为新创建的对增加了一个名为haircolor的属性,并没有影响到Asian.prototype。 constructor • 返回一个对象的构造器引用 Js代码 收藏代码 function Asian(){}; new Asian().constructor===Asian;//true • constructor 是 prototype 的一个属性,如果 A 对象的 prototype 是 B 对象的引用,那么 A 、 B 两个对象的 constructor 是一样的。这时我们需要这么做: Js代码 收藏代码 function A(){} function B(){} A.prototype=new B(); A.prototype.constructor=A; ‘’ .constructor===String//true [].constructor===Array;//true 1..constructor===Number;//true ,第一个点是小数点 (1).constructor===Number;//true true.constructor===Boolean;//true var foo=function(){}; new foo().constructor===foo;//true • 我们可以用这种方式精确判断一个对象的类型,比 typeof 和 instanceof 好的多 • typeofof 只有‘ undefined ’ , ‘ object ’ , ’ string ’ , ’ boolean ’ , ’ number ’ , ’ function ’ 6 个值,而 instanceof 的做法与 java 的 instanceof 一样 arguments arguments 是 js 函数参数对象,它是一个奇怪的对象。 arguments不是全局对象的属性,它是当前函数的一个属性function(){alert(arguments===arguments.callee.arguments);}//显示true 但是只有arguments可以直接取得,当前函数的其它属性无法直接取得,依然只能使用下述方式取得:函数名.属性名、函数名['属性名']、arguments.callee.属性名、arguments.callee['属性名'] 而且发生递归时,每次递归都会得到不同的arguments对象。 它的属性如下: length ——调用时传入函数的参数数目 callee ——函数本身的引用 arguments[INTEGER_INDEX] ——获取相应的实参值,实参索引与传入函数时的位置一一对应。 INTEGER_INDEX>=0&&INTEGER_INDEX<arguments.length arguments.callee 会强迫 js 解释器查找当前函数的自身的引用 function foo(){} // 如果 foo 是局部函数, caller 是 undefined // 如果 foo 是全局函数, caller 是全局对象 // 如果 foo 是一个对象的方法, caller 与 foo 内的 this 引用相同 这两个属性会引起时间效率的问题 但在有些时候会很方便。我们看下面的例子 setTimeout(function(){ setTimeout(arguments.callee,0); },0); // 在需要递归调用一个匿名函数的时候使用 arguments.callee 会很方便 • 同时可利用 callee 和 caller 在运行时动态创建或修改函数对象的属性值 Js代码 收藏代码 var foo=function(){ var callee=arguments.callee; if(!callee.invokeCount){callee.invokeCount=0;} callee.invokeCount++; var caller=callee.caller; if(caller.invokeFoo){caller.invokeFoo=0;} caller.invokeFoo++; }; • Array.apply([],arguments);//可以用这种方式将arguments转换成数组 判断一个函数是不是 new 调用 Js代码 收藏代码 // 现在利用前面说过的 this callee caller 特性判断一个函数是不是在调用时使用了 new function foo(){ if(this.constructor===arguments.callee){ // 是用 new 调用的 }else{ // 不是,此时 (constructor===undefined)===true } }// 我们可以用这种方式使用一个 Function 对象即能做函数又能做构造器 Function 的 prototype • 任何 function 声明的对象都是 Function 对象,我们可以利用这一特性完成一些特殊应用 Js代码 收藏代码 Function.prototype.inherit=function(name,fun){ if(arguments.length===2){ this.prototype[name]=fun; }else{ this.prototype=name; } return this; } var o=(function(){}).inherit({a:1}).inherit( ‘ name ’ ,function(){}); new o().name(); new o().a; 基于闭包的偏函数应用 Js代码 收藏代码 var fn=function(args){ // 初始化工作 // 这部分代码只执行一次 return (fn=function(args){ // 需要重复执行的代码, // 会用到前面初始化的一些变量 })(args); }// 这种用法可以有以下应用场景 // 第一次调用时传入比较多的参数,把一部分参数设为固定值,以后再调用时可传入比较少的参数。 防止递归溢出的方法 Js代码 收藏代码 var bounce=function(recursion){ for(;recursion.constructor===Function; recursion=recursion()); return recursion; }; var recursion=function(arg){ return function(){return arguments.callee(arg);}; }; bounce(recursion()); // 这是个无穷递归,然后被改造成了无限循环。 //chrome 递归层次最少只有 2000 多,所以可以增加一个递归计数器,在计数器达到 2000 时采用这种方式 其它 • hasOwnProperty 与 in 关键字 • if(VAR_NAME in OBJ){} 会判断 VAR_NAME 是不是存在于 OBJ 的 prototype 链上 • for(var n in obj){} 会遍历 obj 整个 prototype 继承链 • 为了判断属性名是 obj 自身的属性还是 prototype 的属性应该使用 obj.hasOwnProperty(n) • propertyIsEnumerable 表示属性名是不是可枚举的 • 如果它返回 true ,在 for in 循环中就会被遍历到 • 如果要为一个对象声明的属性名是 js 保留字,应该这么做: foo[ ‘ class ’ ]= ‘ val ’ ; Js代码 收藏代码 function foo(a){// 可用这种方式指定默认值 a=a|| ’ defaultVal ’ ; } function foo(a){ var b=(a && a.constructor===Number && a*10)||0; // 可用这种方式初始化变量,省去复杂的 if 判断 } 事件模型 • Js 是基于事件驱动的。包括 setTimeout 和 setInterval 也是 • setInterval 会每隔指定时间触发事件,把 interval 函数置于事件队列尾,不论之前的 interval 函数有没有执行完或有没有执行 • setTimeout 会在到达指定时间时触发且仅触发一次 timeout 函数 • Ajax 会在响应结束后触发事件,并把响应函数置于事件响应队列尾 • 所有事件响应函数都在同一个事件队列里面依次执行 <!--[if !supportLists]-->• <!--[endif]-->而事件队列属于界面线程 原生对象构造器 Function • Function 是所有函数对象的构造器。 • 实际上我们使用 function 关键字定义一个函数时 js 解释器调用的就是 new Function 创建一个新的函数对象 • 当然并不推荐在 js 编程实践中出现 new Function 这样的代码,一方面会降低代码可读性,另一方面会在加载时增加传输字节数,第三会降低 js 解析效率 • 至于第三条的原因,个人猜想应该是 new Function 会强迫 js 解释器在执行上下文中再次调用 js 语法分析器,这样当然不如直接使用 function(){} 声明,在 js 加载完成时一次分析完成所有代码构造出所有对象来的快。 Function.apply(argsArray.concat(codestring))。由于Function的最后一个参数才是函数体,前面的参数都是函数参数,有时如果不得不通过Function动态构造函数,而参数名又都保存在数组里,就可以用这种方式创建一个新函数。Function前面不需要用new。 Array • 由于 Array 构造器的参数比较混乱,个人猜想应该是 js 解释器内建多个重载的 Array 构造器 • new Array(1,2,3) <==>[1,2,3] • new Array(len) <==>[]//len 可以任意整数 • new Array( ‘ 1 ’ ) <==>[ ‘ 1 ’ ] • 建议任何时候都用数组字面量表示法定义数组 RegExp • alert('a12b5678'.replace(/\d+/g,'3$&4'));//$&引用被匹配的子串 • /;$/.test('var a=1;');//浏览器总是从字符串开头逐字符匹配正则表达式直到字符串尾,在所有正则表达式分支都尝试之后匹配才会结束,所以这个正则表达式很慢 • $1到$9会在replace的第二个参数中引用正则表达式匹配到的子串,$1匹配表达式中第一对括号匹配到的子串,依次类推 • var s=''.replace(/^\s+/,''); for(var i=s.length-1;i>=0&&/\s/.test(s.charAt(i));i--); s=s.substring(0,i+1); //与''.replace(/^\s+/,'').replace(/\s+$/,'')相比,前者在处理长字符串结尾短空白时更快,但是尾空白太长时并不比后者理想 //这是最快的两种去首尾空白方案,jquery使用后者,有些书上推荐前者 Boolean String Number 等 value class typeof "foo" String string new String("foo") String object 1.2 Number number new Number(1.2) Number object true Boolean boolean new Boolean(true) Boolean object /abc/g RegExp object (function in Nitro/V8) new RegExp("meow") RegExp object (function in Nitro/V8) • 每次用 new 创建 Number String Boolean 对象实际上又为实际值包装了一层引用,类似 java 的 new String(“”) • 建议只把它们用作类型转换函数,比如: Number(‘1’) Boolean(‘true’) 由于 js 是弱引用类型,在进行计算时, js 解释器总是试图把同一表达式内不同类型的值进行自动类型转换, 而不会抛出错误 下面的列表足以说明类型转换的混乱,如果应用了这些特性,会增加调试与维护代码的难度,和出错的可能。 Js代码 收藏代码 "" == "0" // false 0 == "" // true 0 == "0" // true false == "false" // false false == "0" // true false == undefined // false false == null // false null == undefined // true " \t\r\n" == 0 // true "" === "0" // false 0 === "" // false 0 === "0" // false false === "false" // false false === "0" // false false === undefined // false false === null // false null === undefined // false " \t\r\n" === 0 // false === !== 与 == != 就像孪生的兄弟姐妹,一半天使一半恶魔 有时我们需要显式类型转换,前面已经提到可用 Number Boolean 等进行显式转换下面还有一些类型转换方式 转化成数字 —— 不推荐使用 Js代码 收藏代码 +'010' === 10 parseInt('010', 10) === 10 // 用来转换为整数 +'010.2' === 10.2 parseInt('010.2', 10) === 10 parseInt(‘1a’,10)===1;// 这是糟糕的特性 转化字符串 Js代码 收藏代码 ‘’ + 10 === ‘10’; // true [‘aaa’,1234,true].join(‘’);// 连接字符串 ,ie 比较快 var repeatString=function(strToRepeat,repeat){return new Array(repeat+1).join(strToRepeat)}; // 可用这种方式构造重复 repeat 次的字符串,这是 new Array 的惟一用处 转化成 Boolean ,这种方式更快 Js代码 收藏代码 !!‘foo’; // true !!‘’; // false !!‘0’; // true !!‘1’; // true !!‘-1’ // true !!{}; // true !!true; // true !!undefined ===false !!null===false !!0===false !!1===true !!-1===true !!function(){} //true 关于类型转换的其它内容 这部分不好分类,就放在这里 对两个对象进行比较或四则运算时, 如果是需要优先转化为数值的会调用对象的valueOf方法,如果是优先转换字符串的会调用对象的toString 其中之一是Date、RegExp、String会优先转换字符串 Boolean与Number一起会转换成Number,Boolean与其它类型会优先转换字符串 自定义对象可实现valueOf和toString完成自定义对象的默认类型转换方式 分号 Js 解析器只能解释以分号结束的语句,如果没有加上分号解析器会尝试自己加上分号再解析。所以对于没有分号的 js 可能会出现不符合预期的结果。 为了降低调试难度,应该每条语句都以分号结束。 return 所在的行必须有分号,返回值不能在 returnr 的下一行,比如: Js代码 收藏代码 return 1;//unreachable Js代码 收藏代码 return {A:1 };//valid eval setTimeout setInterval eval 调用 js 解析器将字符串参数解析为 js 语句并执行,造成的性能影响与前面说过的 new Function 问题一样 setTimeout setInterval 的第一个参数既可以是 function 也可以是字符串,当传递字符串时也会发生同样的事情。 所以强烈不建议使用 eval ,也不要向 setTimeout 和 setInterval 传递字符串 良好编程规范 1. 不用 for in 遍历数组,因为 for in 会遍历原型对象 2. 不定义全局变量。 3. 每条语句以分号结束 4. 尽量不用保留字 5. 不用 typeof 和 instanceof 6. 不用 parseInt 7. 不使用 == 和 != 8. 不使用 with 表达式 9. 不用 eval new Function 10. 不向 setTimeout setInterval 传递字符串 11. 少用 continue 会提升性能 12.for if else while 一律使用 {} 包含语句块 13. 使用 var fn=function(){}, 代替 function fn(){} 因为后者会定义全局函数 14. 不使用 new String new Boolean new Number new Array 15. 尽量少用 callee caller 16. 尽量减少闭包层次和继承层次 17. 对于使用两次以上的对象属性和闭包变量,一定先使用局部变量取得属性值,减少对象属性访问次数 18. 如果必须使用全局对象,一定先用局部变量引用全局对象 19. 不要修改原生对象 生僻浏览器 dom api 1.Window.navigator// 浏览器与系统信息只读 2.document.getElementsByTagName//NodeList 对象,结果集会随着 dom 树的改变而改变 3.UserAgent ,浏览器内核与版本信息 4.attachEvent||addEventListener // 向 dom 对象添加事件响应函数 detachEvent||removeEventListener // 从 dom 对象移除事件响应函数 5. Js代码 收藏代码 var css=dom.currentStyle||window.getComputedStyle(dom,null);// 获取 css 文件定义的样式 var bind=function(dom,e,f){dom.attachEvent?dom.attachEvent(‘on’+e,f):dom.addEventListener(e,f,null);}; var unbind=function(dom,e,f){dom.detachEvent?dom.detachEvent(‘on’+e,f):dom.removeEventListener(e,f,null);}; var load=(function(fn){ return function(e){ e=e||window.event; var tar=e.target||e.srcElement; if(e.readyState || e.readyState===‘loaded’|| e.readyState===‘completed’){ unbind(tar,’load’,load); unbind(tar,’readystatechange’,load); fn(e); } }; })(function(){}); bind(dom,’load’,load); bind(dom,’readystatechange’,load); 6. dom.cloneNode();//只复制dom dom.cloneNode(true);//dom的子节点一起复制 Ie 内存泄漏 1. 循环引用 Js代码 收藏代码 var myGlobalObject; function SetupLeak() { // First set up the script scope to element reference myGlobalObject = document.getElementById("LeakedDiv"); // Next set up the element to script scope reference document.getElementById("LeakedDiv").expandoProperty = myGlobalObject; } //myGlobalObject 引用了 LeakedDiv,LeakedDiv 又引用了 myGlobalObject function Encapsulator(element) { // Set up our element this.elementReference = element; // Make our circular reference element.expandoProperty = this; } function SetupLeak() { // The leak happens all at once new Encapsulator(document.getElementById("LeakedDiv")); } function BreakLeak() { document.getElementById("LeakedDiv").expandoProperty = null; } //Encapsulator 的对象引用了 element , element 又引用了 Encapsulator 造成泄漏 2. 闭包 Js代码 收藏代码 function AttachEvents(element) { // This structure causes element to ref ClickEventHandler element.attachEvent("onclick", ClickEventHandler); function ClickEventHandler () { // This closure refs element } } function SetupLeak() { // The leak happens all at once AttachEvents(document.getElementById("LeakedDiv")); } // ClickEventHandler 里面引用了 element , element onclick 事件又引用了 ClickEventHandler // 这也是循环引用的一种 // 在事件响应函数内部应该使用 e.srcElement 取得事件触发对象 // 同时应该使用 attachEvent 绑定事件响应函数 3. 添加子节点 Js代码 收藏代码 function LeakMemory() { var hostElement = document.getElementById("hostElement"); // Do it a lot, look at Task Manager for memory response for(i = 0; i < 5000; i++) { var parentDiv = document.createElement("<div onClick='foo()'>"); var childDiv = document.createElement("<div onClick='foo()'>"); // This will leak a temporary object parentDiv.appendChild(childDiv); hostElement.appendChild(parentDiv); hostElement.removeChild(parentDiv); parentDiv.removeChild(childDiv); parentDiv = null; childDiv = null; } hostElement = null; } // 先向父节点内添加子节点,最后把父节点添加到根会造成泄漏,把顺序反过来添加顺序就不会 4.Dom 的 text 属性 Html代码 收藏代码 <head> function LeakMemory() { // Do it a lot, look at Task Manager for memory response for(i = 0; i < 5000; i++) { hostElement.text = "function foo() { }"; } } </head> <body> <button onclick="LeakMemory()">Memory Leaking Insert</button> <script id="hostElement">function foo() { }</script> </body> <!-- 不断修改一个 dom 的 text 属性会造成内存泄漏-->
相关推荐
这份"javascript相关知识20190723.zip"压缩包文件包含了关于JavaScript核心概念的一些关键知识点,如数据类型、运算符、对象创建以及数组的使用。接下来,我们将深入探讨这些主题。 一、JavaScript数据类型 ...
本教程是关于JavaScript的英文版初学者指南,详细介绍了...同时,教程还建议读者访问***/javascript了解更详细的JavaScript相关知识。尽管提供的内容是英文版,但它被认为是“很不错”的教程,适合初学者进行学习。
1. JavaScript 的基础知识 在学习 JavaScript 之前,你需要了解 HTML 和 XHTML 的基础。JavaScript 被设计用来向 HTML 页面添加交互行为,通过在 HTML 中插入 JavaScript 代码,可以实现页面元素的动态操作和用户...
以下是根据标题、描述和标签推测的JavaScript相关知识点的详细说明: 1. **基础语法**:JavaScript的基础包括变量声明(var、let、const)、数据类型(如字符串、数字、布尔值、null、undefined、对象、数组)、...
期末结课大作业 html+css+javascript网页设计实例 企业网站制作,资源里面有网页的HTML文件、CSS文件、JAVASCRIPT文件和网页中的图片文件,用于大学web网页课程设计参考以及相关从业人员参考学习
9. **CHM格式**:所有的文件都是CHM格式,这是微软的一种帮助文件格式,包含索引、搜索功能,方便用户快速查找和学习JavaScript相关知识。 综上所述,这个压缩包是一个全面的JavaScript学习资源库,涵盖了从基础...
javascript知识点汇总
总的来说,这个压缩包提供了一个全面的JavaScript基础知识教程,涵盖了数据类型、Object、内置对象和变量等核心概念,对于初学者或者需要巩固基础的开发者来说,是一份非常有价值的参考资料。通过学习这些内容,可以...
JavaScript面试笔试题...以上就是基于题目内容解析的JavaScript相关知识点,涵盖了语言基础、面向对象编程、异常处理、内存管理和程序设计等多个方面。在准备JavaScript面试时,这些知识点都是需要深入理解和掌握的。
基于Javascript编程基础知识基于Javascript编程基础知识基于Javascript编程基础知识基于Javascript编程基础知识基于Javascript编程基础知识基于Javascript编程基础知识基于Javascript编程基础知识基于Javascript编程...
JavaScript基础知识点总结 JavaScript是一种高级的、动态的、基于对象的客户端脚本语言。它是在网页上执行的脚本语言,能实现网页的交互功能。下面是该资源中的重要知识点总结: 一、 JavaScript 基本概念 * ...
本文讲了Java与JavaScript的区别。主要讲的是JavaScript的基础知识,包括JavaScript是什么,JavaScript的基本语法以及本文运用用大量的实例来讲解各种语法。
javascript 基础知识
javascript 基础知识
javascript 基础知识
javascript 基础知识
javascript 基础知识
javascript 基础知识
JavaScript的运算符知识图谱,有助于你更好的理解JavaScript运算符
javascript 知识点图解 JavaScript 数据类型 、JavaScript 变量、Javascript 运算符、JavaScript 数组、JavaScript 函数基础、DOM 基本操作、Window 对象、JavaScript 字符串函数、正则表达式