六天玩转javascript:javascript变量与表达式(1)
说明
本系列属于进阶系列,语常用语法等不在本系列介绍范围之内。
在我刚开始做一个程序员并开发项目的时候,我总是喜欢使用开发语言的各种特性,每次m$发布新版C#的时候我总是会把开发者预览版下好,亲自体验,并期望从中获得快感,每次使用新的语言进行程序设计的时候,我总是喜欢掌控他有趣的地方,尽管很多与软件工程有悖。这似乎让我出过很多风头,别人的问题似乎没有什么能难倒我的,后来在我开始从工程领域思考问题的时候,我发现很多特点给我带来的"编程快感"引发的麻烦超过了快感本身的价值,很多地方难以被同项目的人理解,这些奇淫技巧在我学习jquery源码的时候也给我带来了不小的麻烦(jq源码使用了大量js技巧),没有相比于更工程化的backbone.js的源码易于学习,这一年我学会了在中间做取舍,这是必须的。
不过我认为我快速学习的能力需要保留,我一般把我学过的高级编程语言分为“面向过程”,“面向对象”,“函数式”,“并发式”,这让我学一个新语言的时候会非常之快速,不过大多数语言都是把特点混杂在一起了。
本系列内容为本人平时项目实践和参照MDN,MSDN,《javascript语言精粹》,《Effective Javascript》等资料,并且整理自己EverNote的日常积累整理所写,时间太长有一些代码样例来源不可考。本系列不允许任何形式的转载,谢谢。 from yeanzhi
大纲
第一天:javascript变量,操作符与变量作用域
第二天:javascript函数
第三天:对象与原型
第四天:再谈函数与继承
第五天:内置对象
第六天:特殊性质与技巧
第一天:javascript变量与表达式(1)
JavaScript 是一种面向对象的动态语言,它包含类型、运算符、核心对象(core objects)和方法。不过需要注意的一个主要区别是 JavaScript 不支持类,类这一概念在 JavaScript 通过对象原型(object prototype)实现。另一个主要区别是 JavaScript 中的函数也是对象,JavaScript 允许函数在包含可执行代码的同时,能像其他对象一样被传递。
数字
1,JavaScript 采用“IEEE 754 标准定义的双精度64位格式”,也就是说不区分整形和浮点数,js中一切数字都是用浮点数表示。
2,js中算数除了+,-,*,/等常规运算符外,在Math中还定义了一些高级数学函数,在使用之前请确定传入的参数是数字,可以使用Numbetr(),parseInt(),或者parseFloat()转化成数字并用isNaN()来判定,如果返回的是数字,那么!isNaN(theNumber)将返回true。
注意:Number()的强制类型转换与parseInt()和parseFloat()方法的处理方式相似,只是它转换的是整个值,而不是部分值。parseInt()和parseFloat()函数会尝试逐个解析字符串中的字符,直到遇上一个无法被解析成数字的字符,然后返回该字符前所有数字字符组成的数字。如“3.4.5”被转换成“3.4”,用Number()进行强制类型转换将返回NAN,同理使用运算符 + 将字符串转换成数字,只要字符串中含有无法被解析成数字的字符,该字符串都将被转换成 NaN。如果字符串值能被完整地转换,Number()将判断是调用parseInt()还是parseFloat()。
字符串
JavaScript 中的字符串是一串字符序列。更准确地说,它们是一串由Unicode 字符构成的字符序列,每一个字符由一个 16 位二进制数表示。
布尔类型
这个类型的变量有两个可能的值,分别是 true 和 false(两者都是关键字)。根据具体需要,JavaScript 按照如下规则将变量转换成布尔类型:
1,false、0、空字符串("")、NaN、null 和 undefined 被转换为 false,
2,其他值被转换为 true
其他类型
JavaScript 中 null 和 undefined 是不同的,前者表示一个空值(non-value),后者是“undefined(未定义)”类型的对象,表示一个还没有被分配的值。不过undefined==null 返回的是true,也就是说在实际编程中他俩误用对代码几乎没什么影响。
变量
1,在 JavaScript 中声明一个新变量的方法是使用关键字 var,
2,在js1.7中 let 关键字被引入,我们可以使用 let 语句声明一个变量,该变量的范围限于声明它的块中。 可以在声明变量时为变量赋值,也可以稍后在脚本中给变量赋值。
使用 let 声明的变量,在声明前无法使用,否则将会导致错误。
如果未在 let 语句中初始化您的变量,则将自动为其分配 JavaScript 值 undefined。
var i = 10;
{
let i = 2;
// 这, i = 2.
}
// 这里, i = 10.
通过对比let和var更有助于理解这个关键字
1,全局中使用这两个没有任何差别
let me = 'go'; //和下面等同
var i = 'able';
2,函数作用域里面
function ingWithinEstablishedParameters() {
let terOfRecommendation = 'awesome worker!';
var sityCheerleading = 'go!'; //没什么区别
};
3,块作用域中这里是差别
function allyIlliterate() {
//变量tuce 在这里不可用
for( let tuce = 0; tuce < 5; tuce++ ) {
//tuce is only visible in here (and in the for() parentheses)
};
//变量tuce 在这里不可用
};
function byE40() {
//变量nish 在这里可用
for( var nish = 0; nish < 5; nish++ ) {
//nish is visible to the whole function
};
//变量nish 在这里可用
};
4,let还可以创建自己的封闭块,当然一遍我不推荐这样
function conjunctionJunctionWhatsYour() {
//变量sNotGetCrazy 在这里不可用
let( sNotGetCrazy = 'now' ) {
//sNotGetCrazy is only visible in here
};
//变量sNotGetCrazy 在这里不可用
};
综述:主要的区别是 var 变量的范围是整个封闭函数
变量作用域(这里不讨论1.7引入的let)
js很容易在全局命名空间中创建变量,定义全局变量会污染共享的命名空间,并且可能导致意外的冲突,同时全局变量不利于模块化,它会导致程序中独立的组件不必要的耦合,在 code now and organize later 的时候可能比较方便,但优秀的程序员会不断的留意程序的结构,持续的归类相关的功能,以及分离不必要的组件,并将这些作为编程过程中的一部分。
js的全局命名空间也被暴露为在程序全局作用域中可以访问的全局变量,这个对象作为this的初始值,web浏览器中全局对象被绑定到全局的window变量,添加或者修改全局变量会自动的更新全局对象
这代表创建一个全局变量有两种方法
1,在全局作用域中使用 var 声明他
2,或者将其添加到全局对象中
var 声明是变量的标准声明
var 声明的变量是永久性的,不能用delete运算符删除
注意:
1,推荐使用var声明的方式,这样会比较清晰表达全局变量在程序中的影响
2,引用未绑定的变量会在运行时有错误,但是程序中给一个未绑定的变量赋值会简单的创建一个新的全局变量
说道作用域,就要提到js中的作用域链的优先级:
嵌套函数的调用对象>调用对象>全局对象
看下面的例子(声明:下面的例子摘自本人EverNote js笔记,应该是来源于互联网,在此感谢作者给出的形象例子)
eg1.
var scope="global";
function f(){
alert(scope);
var scope="local";
alert(scope);
}
f();
过程:
创建全局对象,搜索函数外的var声明语句,在全局对象中创建scope属性,scope=undefined
创建全局的执行环境,作用域链只有一个对象:全局对象
依次执行代码:
var scope="global"时,变量名解析开始,在全局对象属性中查找scope属性
把"global"赋给scope
遇到函数调用:创建调用对象
搜索函数中的var声明语句和参数,在调用对象中创建scope的属性,scope=undefined
创建函数执行环境,作用域链:调用对象>全局对象
依次执行代码:
alert(scope),查询scope,变量名解析,先搜索调用对象,找到scope属性,其值为undefined,执行
var scope="local",查询scope,变量名解析,先搜索调用对象,找到scope属性,scope="local"
alert(scope),查询scope,变量名解析,先搜索调用对象,找到scope属性,其值为"local",执行
eg2.
var scope="global";
function f(){
alert(scope);
scope="local";
alert(scope);
}
f();
过程:
创建全局对象,搜索函数外的var声明语句,在全局对象中创建scope属性,scope=undefined
创建全局的执行环境,作用域链只有一个对象:全局对象
依次执行代码:
var scope="global"时,变量名解析开始,在全局对象属性中查找scope属性
把"global"赋给scope
遇到函数调用:创建调用对象
搜索函数中的var声明语句和参数,没有找到var声明语句
创建函数执行环境,作用域链:调用对象>全局对象
依次执行代码:
alert(scope),查询scope,变量名解析,先搜索调用对象,没找到scope属性,再搜索全局对象,找到scope属性,其值为"global"执行
scope="local",查询scope,变量名解析,先搜索调用对象,没找到scope属性,,再搜索全局对象,找到scope属性,scope="local"
alert(scope),查询scope,变量名解析,先搜索调用对象,没找到scope属性,再搜索全局对象,找到scope属性,其值为"local",执行
eg3.
scope1="global";
alert(scope1);
function f(){
alert(scope2);
scope2="local";
}
f();
过程:
创建全局对象,没有找到var声明语句,没有自定义的全局对象属性
创建全局的执行环境,作用域链只有一个对象:全局对象
依次执行代码:
scope1="global"时,变量名解析开始,作用域链是没有找到scope1属性,在全局对象属性中创建scope1属性,并赋值为"global"
alert(scope1)时,变量名解析开始,作用域链是找到scope1属性,其值为"global",执行
遇到函数调用:创建调用对象
搜索函数中的var声明语句和参数,没有找到var声明语句
创建函数执行环境,作用域链:调用对象>全局对象
依次执行代码:
alert(scope2),查询scope2,变量名解析,作用域链是没有找到scope2属性,报错scope2 is not defined
闭包
说道作用域,就必须要提到“闭包”这个特性,闭包(Closure)是词法闭包(Lexical Closure)的简称,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外,这就叫闭包。
在 ECMAScript 中使用全局变量是一个简单的闭包实例。
为了更好的理解闭包,我们要记住三点:
1,js允许引用在当前函数以外定义的变量
function makeSandwith(){
var magicIngredient = "peanut butter";
return function(filling){
return magicIngredient+" and "+filling;
}('jelly')
}
//console: peanut butter and jelly
2,即使外部函数返回,js也允许当前函数引用在外部函数所定义的变量
function makeSandwith(){
var magicIngredient = "peanut butter";
return function(filling){
console.log(magicIngredient+" and "+filling);
};
}
var res = makeSandwith();
res('jelly')
res('jelly')
res('jelly')
上面代码构造闭包的字面量语法叫做“函数表达式”,该函数是匿名的,我们只需要它能产生一个新的函数值,而不打算在局部掉用它。
3,闭包可以更新外部变量的值,实际上闭包存储的是一个引用而不是副本
function box(){
var val = undefined;
return{
set:function(newVal){val=newVal;},
get:function(){return val;},
type:function(){return typeof val;}
}
}
var b = box();
console.log(b.type());
b.set('yeanzhi');
console.log(b.get());
console.log(b.type());
console:
undefined
yeanzhi
string
这个例子生成了三个闭包对象,分别是set,get,type,他们都共享val这个变量
下一篇介绍 运算符与操作符