这里有新鲜出炉的 Javascript 教程,程序狗速度看过来!
Javascript 是一种由 Netscape 的 LiveScript 发展而来的原型化继承的基于对象的动态类型的区分大小写的客户端脚本语言,主要目的是为了解决服务器端语言,比如 Perl,遗留的速度问题,为客户提供更流畅的浏览效果。
本篇适合 javascript 新手或者学了前端一段时间,对 js 概念不清晰的同学。本文将讲述几点对于初学者遇到的 javascript 的坑,相信对 javascript 基础薄弱的同学,可以加深对 javascript 的理解
本篇适合 javascript 新手或者学了前端一段时间,对 js 概念不清晰的同学~~。
学习目的
本文针对 javascript 基础薄弱的同学,可以加深对 javascript 的理解。
本文将讲述以下几点对于初学者开说 javascript(有的是大部分语言都有的)的坑
讲解内容如下:
1. 连等
2. i++
3. 包装对象
4. 引用类型
5. && 与 ||
讲解部分
1. 连等
小试牛刀
连等是常见的表达式,但是并不是所有情况都适合连等,连等只适用于字面量并不适用于引用类型。
- // 字面量连等得到想要的结果
- var a,b;
- a = b = 2;
- a // 2
- b // 2
- // 引用类型连等不可预测
- var arr1, arr2;
- arr1 = arr2 = []
- arr1[0] = 10
- arr2[0] // 10
- //引用类型连等使得两个引用指向一个对象,操作其中一个,两个值都变
以上代码是常见的连等,有时候我们需要两个变量同赋值为一个值,我们就这样来操作,但是,如果是引用类型可不能连等赋值哦。
此外,连等赋值会有一个很大的漏洞,就是会将变量泄露到全局中去,上面代码我们没有将其泄露,但看下面代码:
- function fn (num) {
- var a = b = num;
- a // num
- b // num
- }
- fn(10)
- a // 报错
- b // 10
- // 我们并不没有定义全局变量b
可以看到,我们执行了 fn 函数后,全局作用域中出现了 b 变量,这是为什么? 看 var a = b = num 这句话,这句话可以分成两句来看
- var a
- a = b = num
- //只声明了a
我们其实只声明了 a 变量,连等的 b 并没有声明,由此可以知道,b 被挂在了全局的 window 对象上,造成了变量泄露到了全局。
初出茅庐
上面只是简单的例子,接下来我们看一个复杂点的例子
- var a = {
- x: 1
- }
- var b = a a.x = a = {
- y: 1
- }
- a.x // undefined
- b.x // {y: 1}
这个例子是在一个测试题中看到的,乍一看,好像不明觉厉,但是一点都不难理解。
1. a 和 b 是引用了类型,同指向了一个对象 {x: 1}
2. a.x 引用了原对象的 x 属性, a 则为一个引用变量
3. a = {y: 1} 只是将 a 这个引用变量的指针指向了另一个对象 {y: 1}
4. a.x = a,前者还是代表着原来的对象的 x 属性,也就是 b 引用的对象的 x 属性
5. 赋值完毕。
可能你还没有理解,不要急,下面我们将解剖 javascript 引擎让你懂的明明白白
庖丁解牛
引擎的工作原理: 引擎在解析 javascript 表达式时,会进行 LHS 查询, RHS 查询(详见《你不知道的 javascript》),我将它们理解为 LHS(赋值),RHS(查找)。
下面,就上面例子,我们来演示一下引擎的工作流程
- var a = {
- x: 1
- }
- // 引擎:我将要对a变量LHS(赋值),内容是{x: 1}
- // 作用域: 刚声明了a变量,给你。
- var b = a
- // 引擎: 我将要对a变量RHS(查找)
- // 作用域: 你刚刚给它LHS了,给你吧
- // 引擎: 我将要对b变量LHS(赋值),内容为a变量指向的对象
- // 作用域:刚声明了b变量,给你。
- a.x = a = {
- y: 1
- }
- // 引擎:我将要对a进行LHS(赋值),内容是另一个对象{y:1}
- // 作用域:可以,给你,但好像还有其他命令,先不要赋值。
- // 引擎: 是的,下一步我还需要对a.x 进行LHS(赋值),内容是将要改变的a变量
- // 作用域: 可以,a变量指向的对象有x属性,不过马上a就改变了,不过没关系,b变量也指向那个对象,赋值完后,你可以用b变量引用旧对象。
- // 引擎:了解了,我先把a变量赋值为一个新的对象,然后把原来的a变量指向的对象的x属性赋值为 新a。
- a.x // undefined
- // 引擎: 我需要拿到a变量指向的对象的x属性
- // 作用域: 你刚刚改变了a的指向,现在的a指向的对象已经没有x属性了
- b.x // {y: 1}
- // 引擎: 我需要拿到b变量指向的对象的x属性
- // 作用域: 你是想拿到你旧对象的x属性吧,给你,不过已经被你在之前改变了值,现在b.x的值就是a指向的新对象的值。
以上是我的理解,没有权威认证。想详细了解执行过程,请参考《你不知道的 javascript》,如果本节有误,请指出。
2. ++ 操作符
大家最很常用 ++ 操作符,其实也没什么很大奇特之处,但是对于新手入门的你是否真正了解他。
- var a = 1;
- var b = a++
- a // 2
- b // 1
- var c = 1;
- var d = ++ c;
- c // 2
- d // 2
前 ++ 和后 ++,一个是返回表达式自增后一个是返回表达式自增前的值。我们可以把两个进行分解,看一下过程。
- b = a++
- // 等价于 ...
- b = a
- a = a + 1
- //.........................
- b = ++ a
- // 等价于 ...
- a = a + 1
- b = a
只是一个运算顺序问题,这个可能好理解,但是也有一个坑,如下。
前几天一个人问: 1++ 等于几?答: 2
估计很多人第一反应就是 2,但是这大错特错!那为什么不等于 2 呢,其实 1++ 是报错了,并不是合法的表达式,原因如下:
- 1 ++
- // 等价于
- 1 = 1 + 1
- // 引擎对 1 进行LHS(赋值),作用域发现他是非法变量,所以会报错 左值无效。
3. 包装对象
我们在用字符串获取长度、使用方法截取等行为时,你有没有想过: 字面值只是个值,为什么他会有方法属性,不是应该对象才有的吗?的确是对象才有的,但是在执行表达式时,产生了包装对象。也许你看过了此知识点,可以跳过。
- var str = 'hello'
- str.length // 5
- str.aaa = 5
- str.aaa // undefined
我们定义一个 str 字符串,获取长度为 5,但是我们自己加一个属性 aaa 缺获取不到,这需要用包装对象的声明周期来解答:包装对象的声明周期只存在于一个表达式内
- var str = 'hello'
- str.length
- // 等价于
- new String(str).length
- str.aaa = 5
- //等价于
- new String(str).aaa = 5
- str.aaa
- // 等价于
- new String(str).aaa
也就是说,每一次用到 str 属性时,都是先包装成 String 对象,操作完后,对象释放,可见,以上两次 str.aaa 是不同的对象,所以第二次获取 aaa 属性当然没有了。不了解可以百度一下 js 包装对象,有详细的解答。
4. 引用类型
大部分语言都有引用类型,其实就是对象变量。c 语言中,我们将引用类型理解为指针,这个指针是动态指向一块内存,通过代码的变化,指针的指向会随之改变。js 也一样。
在我们书写代码中,一定要记住引用类型变量和字面值变量的 区别,他们分别有不同的用处。
- var global = new Object()
- function setColor (obj) {
- obj.color = 'blue'
- obj = new Object()
- obj.color = 'red'
- }
- setColor(global)
- global.color // blue
这是《javascript 高级程序设计》里面的一个例子,我们传递一个对象 global 到 setColor 函数里,在内部进行了如上操作,我们打印出 global.color 是 blue,为什么不是 red?这里就是引用类型的结果。
1. global 变量是引用类型,他指向一个对象,
2. 传递到 setColor 函数中,obj 就引用了 global 指向的对象(下面称为 globalObj)
3. 给 globalObj 赋值一个 color 属性为 blue 字符串,这时 global.color 是 blue 了
4. 将 obj 指向另一个新对象 localObj,使得 obj 与 global 断开连接。
5. 将 localObj.color 赋值为'red'
可以看出,我们并没有对 global 对象的 color 进行'red'赋值,'red'赋值给了另一个对象的 color 属性。
结论:引用类型传递是将两个变量指向同一个对象,而字面量的传递仅仅是值的赋值传递。我们可以将引用类型传递到函数进行改变,不可以在函数内改变传递进来的字面值。
5. && 与 ||
两者的基本运用相信大家都了解,大部分用来 if 判断,如:
- var a = 2;
- var b = false
- if (a && b) {
- alert('best')
- }
- if (a || b) {
- alret('good') // 运行
- }
他们的用法不局限于判断 左右两边的 && 和 || 关系,还可以用来提供代码的质量
- var obj = {}
- if (obj.info.user === 'xu') { // terrible
- // ..
- }
- if (obj.info && obj.info.user === 'xu' ) { // good
- // ...
- }
如果仅仅判断 obj.info.user 会报错造成程序终止,但是下面那样判断就大使得代码健壮,不会那么容易崩溃。
重点: && 和 || 并不会返回 true 和 false,他会返回终止此表达式的那个变量值。
- true && 6 // 6
- NaN && false // NaN
- '0' || 6 // '0'
- false || true // true
- false || 0 && 1 // 0
- false || 1 && 0 // 0
&& 和 ||, && 优先级大于 ||。
&& 操作符,如果左边为假,返回左边值,否则,始终返回右边值
|| 操作符,如果左边为真,返回左边值, 否则,始终返回右边值。
结尾
javascript 基础本章简单的介绍在这里,内容并不全面,还请多多见谅。如有错误,请指出。。。。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持 phperz!
来源: http://www.phperz.com/article/17/0517/328678.html