记得刚学编程的时候,看知乎萧井陌的文章 ,里边提到一定要先形成整体感,在来填充骨架,这样会在脑子里形成结构,而不是学一点用一点.现在学过python之后,再来学JavaScript,确实深刻的体会到知识迁移的重要性.JS去年在自学的时候看过Head First HTML5 Programming,但是没有系统的学习过,看里边的JS代码也比较费劲,现在学过python,再拿起这本书来看,发现就轻松多了.
当然,JS和Python一样都是一门编程语言,单纯学JS也能和Python一样学很久,而且我把JS的几本经典书也都买了,这次就一鼓作气来搞定JS吧.
JS概述
完整的JavaScript实现由三个部分组成:
- 核心(ECMAScript)
- 文档对象模型(DOM) Document object model (整合js,css,html)
- 浏览器对象模型(BOM) Broswer object model(整合js和浏览器)
JavaScript的解释器是浏览器,只要设备上有浏览器,就可以执行.由于浏览器渲染的是Web页面,所以JS可以插入到HTML文档里,和web页面结合的非常紧密.而且,由于JS是在用户的计算机上执行,在前后端交互的时候,JS起着非常重要的作用.
引入JS代码
引入JS代码有若干方式,最好的方式是将JS代码写在body内最后一个元素,使用<script src = "url"></script>来引入JS代码文件.也可以放在head标签内,但是这样会在DOM模型生成之前执行代码,与其用内置的init,还不如直接将代码写在body最后比较好.
JS的语句最好用分号结束,虽然没有强制要求,但最好要写,这点和python不同.
JS基础
既然JS是一门编程语言,而且支持面向对象,学习编程语言的方法自然还是先要搞懂变量,表达式,基础语句,函数和对象.对于JS来说,还有两个特殊的地方就是DOM和BOM.
变量
JS的变量用 关键字 var 定义.JS的类型和python一样,都是动态类型(变量可以存不同类型的值)和弱类型语言.注意,如果仅用var声明变量而不赋值,则这个变量的值是undefined.
JS的变量命名除了根据python的规则之外,特殊之处在于可以用 $ 开头.推荐用驼峰命名法(第一个单词小写,之后每个单词第一个字母大写)(python推荐用下划线方式命名).
ES6新增了let命令,用于声明变量。其用法类似于var,但是所声明的变量只在let命令所在的代码块内有效。例如:for循环的计数器就很适合使用let命令。
ES6新增const用来声明常量。一旦声明,其值就不能改变。
数据类型
JS的数据类型包括5种简单数据类型和1种复杂数据类型:
- Undefined 未定义
- Null 空
- Boolean 布尔值
- Number 数值
- String 字符串
- Object,本质上由一组无序的键值对构成
使用 typeof 变量名 (不用加括号)来查看变量的数据类型.注意Null的类型是Object.数组返回的是object.
而且null == undefined 的结果是true.这些都是JS不太规范的地方.typeof并不是方法,而是一个运算符.
ES6引入了一种新的数据类型,叫做Symbol,存放独一无二的值.
Undefined类型
Undefined 类型的值只有一个,就是undefined,在使用var关键字声明变量但是未初始化就会是这个值.
这里关键要记住,只要var声明了基本数据类型,就立刻初始化,只使用定义而且初始化之后的变量,这样在代码里判断是undefined类型,就可以立刻知道该变量未定义.
Null类型
Null类型的值也只有一个值,就是null,逻辑角度表示一个空对象指针,用typeof 检测null会返回一个object.
如果要良好的区分null和undefined,则对所有的变量,如果不打算赋值,则使用var xxx = null来声明并赋一个null的值.杜绝使用未声明的变量.说是动态语言,使用起来确实像静态语言.
布尔类型
布尔类型是小写的true和false.可以用内置函数Boolean()来将其他类型转换成布尔值.
空字符串(""),0,null,undefined,NaN都是false类型.
数字类型
JS不区分整型和浮点型,只有一种数字类型.支持以e为底的科学计数法.也支持0开头的8进制和0x开头的16进制赋值方法.
特殊 var x = NaN,用来表示应该返回数字类型的值,但却没有返回(比如不支持的类型转换或者出错).注意,NaN是一个数值类型的值.只要运算里出现了NaN,结果一定就是NaN.
NaN是一个与所有值包括自己都不相等的值.内置函数isNaN()可以用来判断参数是否可以转换成数值,如果不能转换,则结果为true.
通过Number.MIN_VALUE和Number.MAX_VALUE可以看到当前浏览器支持的最大和最小数值,超过这些数值会得到-Infinity和Infinity,无法继续参与运算
字符串类型
字符串在JS里是unicode编码的,用单引号双引号都可以.字符串拼接用+.
除了null和undefined之外,每个数据类型都有一个方法叫做toString(),返回该值的字符串表现,例如:
var a = 334;
var b = a.toString();
b
toString还可以加参数表示基底,常用于将数值按照不同进制转换成字符串显示出来.
字符串的常用方法:
方法 |
说明 |
.length |
返回长度 |
.trim() |
移除空白 |
.trimLeft() |
移除左边的空白 |
.trimRight() |
移除右边的空白 |
.charAt(n) |
返回第n个字符 |
.concat(value, ...) |
拼接 |
.indexOf(substring, start) |
子序列位置 |
.substring(from, to) |
根据索引获取子序列 |
.slice(start, end) |
切片 |
.toLowerCase() |
小写 |
.toUpperCase() |
大写 |
.split(delimiter, limit) |
分割 |
Object类型
这里需要知道的是,和python类似,Object的每个实例的基础都是Object对象,数组,字典都是对象的实例.
如果要单纯定义一个Object类型的数值,可以使用 var o = new Object().
有点像C语言里new一个对象的感觉,Object()是在实例化一个Object对象.
每个Object对象至少都有constructor, hasOwnProperty(propertyname), isPrototypeOf(object), propertyIsEnumerable(propertyname), toLocaleString(), toString(), valueof()方法.
数组 -- 是一种对象
数组有些类似于python 中的列表.
定义用 var a = [1,2,3],也可以用索引去取,如果越界,返回的值是undefined. 数组不像python一样直接用[stard:end]支持切片,必须用slice方法.
数组的常用方法:
方法 |
说明 |
.length |
数组的大小 |
.push(ele) |
尾部追加元素 |
.pop() |
获取尾部的元素 |
.unshift(ele) |
头部插入元素 |
.shift() |
头部移除元素 |
.slice(start, end) |
切片 |
.reverse() |
反转 |
.join(seq) |
将数组元素连接成字符串 |
.concat(val, ...) |
连接数组 |
.sort() |
排序 |
.forEach() |
将数组的每个元素传递给回调函数 |
.splice() |
删除元素,并向数组添加新元素。 |
.map() |
返回一个数组元素调用函数处理后的值的新数组 |
字典 -- 是一种对象
在JS里没有对象,而是用特殊的方式定义:
var person = {
name:"cony",
age:38,
sexy:true
}
这样定义出来在JS里叫做对象.用person.name来取值,而且也支持像字典一样的person["age"]写法.
运算符
大部分与python和其他程序语言相同,记几个特殊的:
运算符 |
说明 |
++ 和 -- |
来自C语言,可以前置和后置,副效果和C也一样,前置先计算结果,再参与表达式求值,后置会在求值之后再递减. |
逻辑与 |
用 && 表示,python里是and关键字 |
逻辑或 |
用 || 表示,python里关键字是or |
相等于不相等 == != |
相等和不等于会先转换数据类型,再进行比较 |
全等于不全等 === !== |
全等和不全等表示两个值在不转换类型的情况下就相等或者不等. |
三元操作符 |
和C语言的一样, a = b>c? 10:20 的形式,表示判断b>c,成立取10,不成立取20. |
流程控制
语句 |
说明 |
if语句 |
if(condition) {statement1} else {statement2},其实和C语言一样.注意即使代码块里只有一行语句,也推荐使用大括号. |
do while语句 |
do {statement} while (condition) 会首先执行一次代码块,再判断条件,但其实都可以用while改写.和C语言写法一样. |
while (condition) {statement} |
和C也一样. |
for语句 |
for之中的内容也和C一样,分为定义初始值,条件,每次改变值的内容三部分,也可以省略三部分中的任意,和C的效果一样. |
for in 语句 |
for(property in expression) {statement}. 与Python一样,用于枚举对象的所有属性. |
label语句 |
给一块语句定义一个标签,将来可以被break或者continue来引用. labelname:statement |
break和continue |
break立刻结束当前循环,继续往下执行,continue是立刻跳到下一次循环的开始处.如果break和continue后边跟着标签名,则会跳到标签的地方.类似于goto语句,不推荐使用,会让代码难以解读. |
switch |
和C语言的一样,通过switch - case 来判断多项条件,每个判断之后都必须接break语句.最后用default语句收尾. |
注意, JS中没有块级作用域,在for循环内定义的变量可以被外部访问到.这个时候其实可以改用let 来定义变量.
函数
函数的定义:与python有些类似,用 function 关键字, 函数名(形参) {函数体来定义}:
function f1(a,b){
return a+b
}
主要要看一下匿名函数:
var sum = function(a, b){
return a + b;
}
sum(1, 2);
在js中如果需要拿到全部传入的参数,需要使用内置对象arguments,在函数内部使用,相当于一个列表,按索引引用即可.
函数可以没有返回值,也可以通过return语句返回值,如果单独写return, 不加任何东西,返回undefined值.return不能够像python一样返回多个值,如果需要返回多个值,只能采用数组或者其他容器.
函数的参数: 和其他语言不同,函数的参数在函数内部是有一个关键字 arguments数组保存的.JS的函数即使定义的时候没有变量或者定义了变量但没有传入适量的值,都不会报错,程序会到这个数组里去找元素,,这个数组是一直存在的.
只不过没有值的元素会给后边的运算带来问题.例如:
function test(){
return arguments[0] + arguments[1];
}
这个函数在使用的时候,test(),test(1),test(1,2),test(1,2,3)都不会报错,只不过没有值的时候相加,结果是NaN.
所以所有的JS函数,天生就能够接收任意多的参数.
此外需要注意的是,符合ECMA标准的JS,基本类型值的参数是按值传递的,而引用类型值的参数是按引用传递的(C系列语言的传值方式).Python里,给函数传参数全部是按照引用方式传递的.但是二者表现出来的结果是一样的,在后边的变量和作用域会提到这一部分.
内置对象和方法
在JS里,所有的东西和Pythonlz类似,也是一切皆对象.JS中实例化对象需要用new关键字,比如声明数组可以采用:
var a = [1,2,3,4,5];
var b = new Array(11,22,33,44);
JS的对象就是引用类型值,可以看高程三的第五章.这里直接开始学习自定义对象.
Object类型--自定义对象
大多数引用类型值都是Object类型的实例, 本质上是键值对的集合(Hash结构), 但是只能用字符串作为键。注意,即使键不用引号括起来,也会被解释成字符串.值如果是字符串,一定要用双引号,为了和JSON保持一致.
var person = {"name":"cony","age":4}
取属性
person.name 直接用属性名引用
person.age
person["name"] 方括号可以用变量来引用
遍历
for (var i in person){
console.log(i)}
动态添加属性
person.gender = "女"
ES6中提供了Map数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当做键。也就是说,Object结构提供了“字符串--值”的对应,Map结构提供了“值--值”的对应,是一种更完善的Hash结构实现(Python中的字典就是完善的Hash实现,键只要是可哈希的值即可)。
Date对象
Date对象用来表示时间,是常用的内置对象.
//方法1:不指定参数
var d1 = new Date();
console.log(d1.toLocaleString());
//方法2:参数为日期字符串
var d2 = new Date("2004/3/20 11:12");
console.log(d2.toLocaleString());
var d3 = new Date("04/03/20 11:12");
console.log(d3.toLocaleString());
//方法3:参数为毫秒数
var d3 = new Date(5000);
console.log(d3.toLocaleString());
console.log(d3.toUTCString());
//方法4:参数为年月日小时分钟秒毫秒
var d4 = new Date(2004,2,20,11,12,0,300);
console.log(d4.toLocaleString()); //毫秒并不直接显示
Json对象
Json格式就是JavaScript语言的标准规范之一,因此JS原生支持Json格式.
Json格式的字符串依然需要遵守一定的规则,比如用双引号,键后的冒号后一个空格,键值对逗号后的空格等.
var str1 = '{"name": "Alex", "age": 18}';
var obj1 = {"name": "Alex", "age": 18};
// JSON字符串转换成对象
var obj = JSON.parse(str1);
// 对象转换成JSON字符串
var str = JSON.stringify(obj1);
这两个方法必须掌握,在交互的时候,频繁使用这两个方法来提交数据.
JS还可以用eval()来定义,但最好不用,可以看W3C的Json使用方法
Math对象
Math对象内封装了一系列方法用于数学运算,直接对要计算的内容使用该模块即可,有点类似于python的Math模块,但是不需要导入.
方法名 |
说明 |
abs(x) |
绝对值 |
exp(x) |
e的指数 |
floor(x) |
向下取整 |
log(X) |
返回自然对数 |
max(x,y) |
返回x和y的最大值 |
min(x,y) |
返回x和y中的最低值 |
pow(x,y) |
返回x的y次幂 |
random() |
返回一个0-1之间的随机数 |
round(x) |
四舍五入 |
RegExp对象
RegExp是JS里的正则表达式.
注意JS里的正则表达式一定中间不能加空格.
// 建立正则表达式对象
var reg1 = new RegExp("^[a-zA-Z][a-zA-Z0-9_]{5,11}$");
// 校验是否匹配
var s1 = "bc123";
reg1.test(s1); # true
// 还可以直接用两个/包含正则表达式,就生成一个正则对象,直接可以进行测试.这个叫做字面量法
/^[a-zA-Z][a-zA-Z0-9_]{5,11}$/.test("bc134_32")
这两个方法的区别在于,如果用RegExp生成正则对象,则类似python传入普通字符串,对于元字符转义,则需要用双重转义,第一层转义给字符串,第二层才交给正则引擎.而采用两个/包含的方法,则类似于python传入raw字符串,只需要一个转义字符即可.
如果test()不传值的时候,会自动传undefined,然后坑爹的是会把undefined转成字符串undefined来进行比较.
将正则与其他字符串方法合起来使用,就可以有各种效果.
var s = "aceracrobat";
var s1 = s.replace("a","xxx"); # xxxceracrobat
//全部替换
var s2 = s.replace(/a/g,"xxx"); #正则后的g表示global,替换全部,类似于python的findall,这样就可以实现全部替换
匹配模式标记有三种,g表示全局,i表示忽略大小写,m表示多行模式,即看到文本的换行符,还会继续查找下一行.
值得注意的是,如果给定一个正则对象,这个对象在g模式下匹配成功某个字符串的话,其内部有个索引会停在上次匹配成功的地方,再调用这个对象的时候,索引不会更新.所以如果一个正则反复用g去匹配的时候,有两种办法,一是不用RegExp对象,而是用字面量法,二是每次开始的时候把索引归零.
变量和作用域
基本类型的值和引用类型的值
在给变量赋值的时候,解释器会确定值的类型,Undefined, Null, Boolean, Number 和 String类型是按值访问的,而对象类型都是按引用访问的.也即操作引用.实际效果和python是一样的.
作用域
与python类似,函数有自己的作用域,从最内向最外一层层找.特别的是,没有块级作用域(针对C系语言来说的,代码块指大括号内的一段代码,这点和python一样,都没用块级作用域,但是都有局部作用域--函数内的作用域),在块内(而非函数内)定义的变量,会成为当前环境的变量.用var 初始化变量的时候,会将该变量添加到最近的环境中,如果没有加var,则自动被添加到全局环境.
后记
JS的基础部分果然是有够混乱的,基础部分对应JavaScript高级程序设计(第3版)的章节是前五章和第七章,面向对象的部分还暂时未涉及 .如果让我写一个以逻辑和计算为主的程序,肯定不会选择JS,因为很多地方太过于随意和混乱,有的时候像在写强类型语言,有的时候又像弱类型语言.但是JS的强大之处在于BOM和DOM模型,能够方便的操作页面元素和事件,这是其他程序语言做不到的.前端开发必须学好JS,没有之一,流行的框架如jQuery,vue.js等都是基于原生JS的.