JS重要知识点小结


Posted in Javascript onNovember 06, 2011

讲解还是以示例代码搭配注释的形式,这里做个小目录:
JS代码预解析原理(包括三个段落);
函数相关(包括 函数传参,带参数函数的调用方式,闭包);
面向对象(包括 对象创建、原型链,数据类型的检测,继承)。
JS代码预解析原理

/****************** JS代码预解析原理 ******************/ 
/* 
JS代码预解析、变量作用域、作用域链等 应该能作为学习JS语言的入门必备知识。 
下边给出些简要解释和一些典型的代码段,若要了解更多,能从网上搜索到更多相关示例。 
引用网上的一段有关 “JS的执行顺序” 的解释: 
如果一个文档流中包含多个script代码段(用script标签分隔的js代码或引入的js文件),它们的运行顺序是: 
步骤1. 读入第一个代码段(js执行引擎并非一行一行地执行程序,而是一段一段地分析执行的) 
步骤2. 做语法分析,有错则报语法错误(比如括号不匹配等),并跳转到步骤5 
步骤3. 对var变量和function定义做“预解析”(永远不会报错的,因为只解析正确的声明) 
步骤4. 执行代码段,有错则报错(比如变量未定义) 
步骤5. 如果还有下一个代码段,则读入下一个代码段,重复步骤2 
步骤6. 结束 
*/ 
// 下边给出 三段觉得比较典型的代码示例: 
/********** 一:基本的几条语句 **********/ 
alert(num); // undefined 
var num = 0; 
alert(str); // 错误:str未定义 
str = "string"; 
alert(func); // undefined 
var func = function (){ alert('exec func'); } 
test(); // exec test 
alert(test()); // 先exec test 后undefined 
function test(){ alert('exec test'); } 
/********** 二:函数名与变量名相同 **********/ 
//var mark = 1; 
function mark(x) { 
return x * 2; 
} 
var mark; 
alert(mark); // function mark(x) { return x * 2; } 
// 去掉前边的var mark = 1;则会返回1 
/********** 三:把第二段包括在语句块中 **********/ 
// 当有条件时候(代码包含在条件语句块里) 
if (false) { 
var mark1 = 1; 
function mark1() { 
alert("exec mark1"); 
} 
//var mark1; 
alert(mark1); 
} 
alert(mark1); 
mark1(); 
// 由于解析浏览器解析不同,这段代码在不同浏览器里执行的结果不一致,具体原因可从网上查找答案

函数相关(包括 函数传参,带参数函数的调用方式,闭包)
/****************** 函数相关 ******************/ 
/********** 一:函数传参 **********/ 
/* 
编程语言大概都有 值类型与引用类型 的区别,JS也不例外。 
原始类型:undefined null number boolean 均为值类型。 
string比较特殊,因为它是不可改变的,String类定义的方法都不能改变字符串的内容。 
function object array 这三种为引用类型。 
*/ 
/* JavaScript 函数传递参数时,是值传递。 
ECMAScript 中,所有函数的参数都是按值来传递的。 
基本类型值的传递和基本类型变量复制一致(采用在栈内新建值), 
引用类型值的传递和引用类型变量的复制一致(栈内存放的是指针,指向堆中同一对象)。 
具体参考:http://www.xiaoxiaozi.com/2010/03/05/1719/ 
*/ 
function setName(obj){ 
//obj拷贝了person的值(person是一个对象的引用地址),所以obj也指向了person所指向的对象。 
obj.name = "xiaoxiaozi"; 
obj = {}; // 让obj 指向了另一个对象 
obj.name = "admin"; 
} 
var person = {}; 
setName(person); 
alert(person.name); // xiaoxiaozi 
/********** 二:带参数函数的调用方式 **********/ 
/* 在DOM不同版本中,函数调用方式不太一样。标准推荐的是addEventListener和attachEvent 
这两种方式有很多资料可查。但是有些已经不被推荐的函数调用仍旧有实际应用,相关资料发现的不多。 
这里主要讨论这些函数调用方式 
*/ 
var g = "全局变量"; 
function show(str) { 
alert("my site: " + str); 
} 
setTimeout("show(g);",100); // g是全局变量,函数正确执行 
function t() { 
var url = "www.xujiwei.cn"; 
var num = 2; 
//setTimeout("alert("+url+")", 3000); // 解析错误,www未定义 
//setTimeout("alert("+num+")", 3000); // 解析正确,注意与上句对比 
//setTimeout("show('url');", 2000); // url 
//setTimeout("show("+ url +");", 2000); // 解析错误,www未定义 
//setTimeout("show(url);", 2000); // 解析错误,url未定义 
//setTimeout('"show("+ url +");"', 2000); // 解析错误,url未定义 
//setTimeout("show('"+ url +"');", 2000); // 正确 
//setTimeout(function(){show(url);},1000); // 正确 
} 
t(); 
/* 结论: 
诸如onclick="xx();"等函数调用方式,在双引号内的内容直接解析为js语句执行。 
若调用的函数带有参数,注意对比以上各种写法,保证传递进去的参数为正确的。 
*/ 
/********** 三:闭包 **********/ 
/* 
闭包,几乎是每个学习JS的朋友都要讨论的问题,因此各种相关资料应有尽有。 
它的作用很大,但也有弊端,例如如果使用不当,容易引起内存泄漏等问题,因此有不少人 
提倡少用闭包。 
这里列出闭包的一种经典应用,一个有争议的应用。 
*/ 
function test1() { //通过闭包,每次能传入不同的j值。 
for (var j = 0; j < 3; j++) { 
(function (j) { 
setTimeout(function () { alert(j) }, 3000); 
})(j); 
} 
} 
test1(); 
/* 这个是闭包的典型应用 */ 
(function tt() { 
for (var i = 1; i < 4; i++) { 
document.getElementById("b" + i).attachEvent("onclick", 
new Function('alert("This is button' + i + '");')); // 在IE中测试 
} 
})() // 立即执行函数,一个文件是否只能有一个?把上边函数写成立即执行出问题,怎么回事? 
/* 这个问题出现在论坛里,有很多争议 
有说是new Function动态生成个闭包结构的函数,所以能保存外部变量。 
有说是跟闭包无关,new Function,就是新定义了一个function, 
i的值也作为这个新的function的参数固化在其内部了。 
*/

面向对象(包括 对象创建、原型链,数据类型的检测,继承)
/****************** 面向对象 ******************/ 
/********** 一:对象创建、原型链 **********/ 
/* 讨论 构造函数(类方式)创建对象 ,深入理解这些内容,是很重要的 
*/ 
function MyFunc() { }; //定义一个空函数 
var anObj = new MyFunc(); //使用new操作符,借助MyFun函数,就创建了一个对象 
// 等价于: 
function MyFunc() { }; 
var anObj = {}; //创建一个对象 
anObj.__proto__ = MyFunc.prototype; 
MyFunc.call(anObj); //将anObj对象作为this指针调用MyFunc函数 
/* 
用 var anObject = new aFunction() 形式创建对象的过程实际上可以分为三步: 
第一步:建立一个新对象(anObject); 
第二步:将该对象内置的原型对象(__proto__)设置为构造函数prototype引用的那个原型对象; 
第三步:将该对象作为this参数调用构造函数,完成成员设置等初始化工作。 
对象建立之后,对象上的任何访问和操作都只与对象自身及其原型链上的那串对象有关, 
与构造函数再扯不上关系了。 
换句话说,构造函数只是在创建对象时起到介绍原型对象和初始化对象两个作用。 
原型链:(参考:http://hi.baidu.com/fegro/blog/item/41ec7ca70cdb98e59152eed0.html) 
每个对象(此处对象应该仅指大括号括起来的object,不包括function、array。待验证?) 
都会在其内部初始化一个属性,就是__proto__,当我们访问一个对象的属性时, 
如果这个对象内部不存在这个属性,那么他就会去__proto__里找这个属性, 
这个__proto__又会有自己的__proto__,于是就这样 一直找下去,也就是我们平时所说的原型链的概念。 
*/ 
/* 理解了对象创建的原理,可试着分析下边两个示例的结果 */ 
var yx01 = new function() {return "圆心"}; 
alert(yx01); // [object Object] 
var yx02 = new function() {return new String("圆心")}; 
alert(yx02); // “圆心” 
/* 解释: 
"圆心"是基本的字符串类型,new String("圆心")则创建了一个string对象。 
只要new表达式之后的构造函数返回一个引用对象(数组,对象,函数等),都将覆盖new创建的对象, 
如果返回一个原始类型(无 return 时其实为 return 原始类型 undefined), 
那么就返回 new 创建的对象。 
参考:http://www.planabc.net/2008/02/20/javascript_new_function/ 
*/ 
/********** 二:数据类型的检测 **********/ 
/* 判断数据类型可能想到的方法: 
constructor、typeof、instanceof、Object.prototype.toString.call() 
*/ 
/***** 1、通过constructor属性 *****/ 
var myvar= new Array("a","b","c","d"); 
function A(){} 
myvar.constructor = A; 
var c = myvar.constructor; 
alert(c); // function A(){} 
//可见,通过constructor属性获取类型的方法很容易被修改,不应该用来判断类型。 
/***** 2、通过typeof *****/ 
/* 
typeof是一个操作符,而不是个函数。 
typeof的实际应用是用来检测一个对象是否已经定义或者是否已经赋值。 
如if(typeof a!="undefined"){},而不要去使用if(a)因为如果a不存在(未声明)则会出错。 
typeof检测对象类型时一般只能返回如下几个结果: 
number,boolean,string,function,object,undefined。 
对于Array,Null,自定义对象 等使用typeof一律返回object, 
这正是typeof的局限性。 
*/ 
var num = new Number(1); 
var arr = [1,2,3]; 
alert(typeof num); //object 而不是number 
alert(typeof arr); //object 而不是Array 
alert(typeof null); // object 
/***** 3、通过 instanceof *****/ 
/* 用instanceof操作符来判断对象是否是某个类的实例。 
如果obj instanceof Class返回true,那么Class的原型与obj原型链上的某个原型是同一个对象, 
即obj要么由Class创建,要么由Class的子类创建。 
*/ 
function t(){}; 
t.prototype = Array.prototype; 
//t.prototype = []; 
var x = new t(); 
alert(x instanceof t);//弹出true 
alert(x instanceof Array);//弹出true 
alert(x instanceof Object);//弹出true 
/* 
由此可知,通过 instanceof 判断数据类型也不可靠。 
因为一个对象(此处x)的原型链可以很长,每个原型的类型可以不同。 
另外在iframe内也会容易出错: 
即有个页面定义了一个数组a,页面又嵌套了一个IFrame,在Iframe里面通过 top.a instanceof Array, 是返回false的。 
这个说明 父页面和内嵌iframe里的对象是不同的,不能混合在一起使用。 
改成top.a instanceof top.Array 就会返回true 
*/ 
/***** 4、通过 Object.prototype.toString.call() *****/ 
/* 
Object.prototype.toString.call() 作用是: 
1、获取对象的类名(对象类型)。 
2、然后将[object、获取的类名]组合并返回。 
可应用于判断Array,Date,Function等类型的对象 
*/ 
var num = new Number(1); 
var arr = [1,2,3]; 
alert(Object.prototype.toString.call(num)); // [object Number] 
alert(Object.prototype.toString.call(arr)); // [object Array] 
// 扩展示例:(apply等价于call) 
window.utils = { 
toString: Object.prototype.toString, 
isObject: function (obj) { 
return this.toString.apply(obj) === '[object Object]'; 
}, 
isFunction: function (obj) { 
return this.toString.apply(obj) === '[object Function]'; 
}, 
isArray: function (obj) { 
return this.toString.apply(obj) === '[object Array]'; 
} 
} 
function A() { } 
window.utils.isFunction(A); //true 
window.utils.isObject(new A()); //true 
window.utils.isArray([]); //true 
/* 
jQuery等框架 就是用这个方法判断对象的类型的,因此可以把这种方法作为权威的判断方法。 
但是,如果重写了Object.prototype.toString方法,这时候再用来判断数据类型可能就会出错, 
所以,一般不要去重写Object.prototype.toString方法。 
*/ 
/********** 三:继承 **********/ 
/* 
JS继承和闭包一样,几乎是每个想深入学习JS的朋友都要讨论的问题,因此各种相关资料应有尽有。 
JS继承代码的版本非常多,但原理都是一样的,核心都是利用了prototype对象。 
为了和其他面向对象语言的风格相似,大多数都采用“类式”风格模拟。 
继承的详细原理不再赘述,网上有许多资料介绍。 
这里给出一个示例:Jquery作者John Resig写的继承。 
(其中的详细注释是来自某个博客,不知道是谁原创,这里私自转帖出来) 
*/ 
(function () { 
// initializing变量用来标示当前是否处于类的创建阶段, 
// - 在类的创建阶段是不能调用原型方法init的 
// - 我们曾在本系列的第三篇文章中详细阐述了这个问题 
// fnTest是一个正则表达式,可能的取值为(/\b_super\b/ 或 /.*/) 
// - 对 /xyz/.test(function() { xyz; }) 的测试是为了检测浏览器是否支持test参数为函数的情况 
// - 不过我对IE7.0,Chrome2.0,FF3.5进行了测试,此测试都返回true。 
// - 所以我想这样对fnTest赋值大部分情况下也是对的:fnTest = /\b_super\b/; 
var initializing = false, fnTest = /xyz/.test(function () { xyz; }) ? /\b_super\b/ : /.*/; 
// 基类构造函数 
// 这里的this是window,所以这整段代码就向外界开辟了一扇窗户 - window.Class 
this.Class = function () { }; 
// 继承方法定义 
Class.extend = function (prop) { 
// 这个地方很是迷惑人,还记得我在本系列的第二篇文章中提到的么 
// - this具体指向什么不是定义时能决定的,而是要看此函数是怎么被调用的 
// - 我们已经知道extend肯定是作为方法调用的,而不是作为构造函数 
// - 所以这里this指向的不是Object,而是Function(即是Class),那么this.prototype就是父类的原型对象 
// - 注意:_super指向父类的原型对象,我们会在后面的代码中多次碰见这个变量 
var _super = this.prototype; 
// 通过将子类的原型指向父类的一个实例对象来完成继承 
// - 注意:this是基类构造函数(即是Class) 
initializing = true; 
var prototype = new this(); 
initializing = false; 
// 我觉得这段代码是经过作者优化过的,所以读起来非常生硬,我会在后面详解 
for (var name in prop) { 
prototype[name] = typeof prop[name] == "function" && 
typeof _super[name] == "function" && fnTest.test(prop[name]) ? 
(function (name, fn) { 
return function () { 
var tmp = this._super; // 这里是必要的,第91行注释代码可说明之。 
this._super = _super[name]; 
var ret = fn.apply(this, arguments); 
this._super = tmp; 
return ret; 
}; 
})(name, prop[name]) : 
prop[name]; 
} 
// 这个地方可以看出,Resig很会伪装哦 
// - 使用一个同名的局部变量来覆盖全局变量,很是迷惑人 
// - 如果你觉得拗口的话,完全可以使用另外一个名字,比如function F()来代替function Class() 
// - 注意:这里的Class不是在最外层定义的那个基类构造函数 
// 这里的Class和上边的window.Class函数不一样,这里是window.Class内部的函数局部变量 
function Class() { 
// 在类的实例化时,调用原型方法init 
if (!initializing && this.init) 
this.init.apply(this, arguments); 
} 
// 子类的prototype指向父类的实例(完成继承的关键) 
Class.prototype = prototype; // Class指代上边的Class,并非一开始的window.Class 
// 修正constructor指向错误 
// 是否可用Class.prototype.constructor = Class;来修正??? 
Class.constructor = Class; 
// 子类自动获取extend方法,arguments.callee指向当前正在执行的函数 
Class.extend = arguments.callee; 
return Class; 
}; 
})();
Javascript 相关文章推荐
在页面上点击任一链接时触发一个事件的代码
Apr 07 Javascript
看了就知道什么是JSON
Dec 09 Javascript
一些有用的JavaScript和jQuery的片段分享
Aug 23 Javascript
JQuery boxy插件在IE中边角图片不显示问题的解决
May 20 Javascript
弹出遮罩层后禁止滚动效果【实现代码】
Apr 29 Javascript
D3.js实现柱状图的方法详解
Sep 21 Javascript
JS实现的tab切换选项卡效果示例
Feb 28 Javascript
Angular 4.x+Ionic3踩坑之Ionic3.x pop反向传值详解
Mar 13 Javascript
vue如何在自定义组件中使用v-model
May 14 Javascript
JS实现数组去重,显示重复元素及个数的方法示例
Jan 21 Javascript
JS数组方法join()用法实例分析
Jan 18 Javascript
如何用vue实现网页截图你知道吗
Nov 17 Vue.js
javascript日期转换 时间戳转日期格式
Nov 05 #Javascript
jQuery News Ticker 基于jQuery的即时新闻行情展示插件
Nov 05 #Javascript
再说AutoComplete自动补全之实现原理
Nov 05 #Javascript
计算世界完全对称日的js代码,粗糙版
Nov 04 #Javascript
在标题栏显示新消息提示,很多公司项目中用到这个方法
Nov 04 #Javascript
由Javascript实现的页面日历
Nov 04 #Javascript
jQuery中jqGrid分页实现代码
Nov 04 #Javascript
You might like
PHP var_dump遍历对象属性的函数与应用代码
2010/06/04 PHP
linux下使用ThinkPHP需要注意大小写导致的问题
2011/08/02 PHP
WordPress自定义时间显示格式
2015/03/27 PHP
JavaScript Undefined,Null类型和NaN值区别
2008/10/22 Javascript
实现png图片和png背景透明(支持多浏览器)的方法
2009/09/08 Javascript
用原生JavaScript实现jQuery的$.getJSON的解决方法
2013/05/03 Javascript
JS实现鼠标单击与双击事件共存
2014/03/08 Javascript
理运用命名空间让js不产生冲突避免全局变量的泛滥
2014/06/15 Javascript
jQuery中:first-child选择器用法实例
2014/12/31 Javascript
利用CSS3在Angular中实现动画
2016/01/15 Javascript
js获取当前日期时间及其它日期操作汇总
2016/03/08 Javascript
用AngularJS来实现监察表单按钮的禁用效果
2016/11/02 Javascript
Jquery中attr与prop的区别详解
2017/05/27 jQuery
Vue计算属性的使用
2017/08/04 Javascript
JS中使用media实现响应式布局
2017/08/04 Javascript
前端跨域的几种解决方式总结(推荐)
2017/08/16 Javascript
vue2.0的虚拟DOM渲染思路分析
2018/08/09 Javascript
vue中v-for循环给标签属性赋值的方法
2018/10/18 Javascript
webpack打包非模块化js的方法
2018/10/24 Javascript
Windows下Node爬虫神器Puppeteer安装记
2019/01/09 Javascript
vue.js this.$router.push获取不到params参数问题
2020/03/03 Javascript
Python中最大递归深度值的探讨
2019/03/05 Python
windows系统中Python多版本与jupyter notebook使用虚拟环境的过程
2019/05/15 Python
PIL包中Image模块的convert()函数的具体使用
2020/02/26 Python
Opencv图像处理:如何判断图片里某个颜色值占的比例
2020/06/03 Python
CSS3 clip-path 用法介绍详解
2018/03/01 HTML / CSS
纯CSS3+DIV实现小三角形边框效果的示例代码
2020/08/03 HTML / CSS
使用HTML5里的classList操作CSS类
2016/06/28 HTML / CSS
Hotels.com南非:酒店预订
2017/11/02 全球购物
六查六看剖析材料
2014/02/15 职场文书
本科毕业生求职自荐信
2014/04/09 职场文书
美术兴趣小组活动总结
2014/07/07 职场文书
大学自主招生自荐信(2016精选篇)
2016/01/28 职场文书
Java实战之用Swing实现通讯录管理系统
2021/06/13 Java/Android
Lakehouse数据湖并发控制陷阱分析
2022/03/31 Oracle
2022新作动画《福星小子》释出宣传影片 加入内田真礼&宫野真守配音演出
2022/04/08 日漫