JavaScript是动态弱类型解释型编程语言, JS解释器称为JS引擎为浏览器的一部分,常用于Web前端编程也可应用于服务端编程等场景.
Js使用单行注释//
和多行注释'/*',*/
两种注释符.
Js使用分号;
或换行标志语句结束, 使用花括号{}
标志代码块, 语义与缩进无关, 允许使用进行折行.
Js标识符区分大小写,以字母或_
, $
开头, 后续字符可以为数字,字母或_
, $
.
Js的运算符与C基本一致, 保留了位运算符和取余%
, 自增++
,减--
和复合赋值运算符.
Js赋值运算符返回右值, 因此允许连续赋值.
> f = function() {
return a = 1;
};
> f()
1
> a = b = 1
1
> a
1
> b
1
因为弱类型的原因,JavaScript的关系运算符比较复杂:
-
==
等于: 值等于或隐式类型转换后等于 -
===
绝对等于: 类型相同且值相等 -
!=
不等于, 与等于取值一定相反 -
!==
不绝对等于, 与绝对等于取值一定相反
示例:
> x = 233
233
> x == 233
true
> x == '233'
true
> x === 233
true
> x === '233'
false
> x != '233'
false
> x !== '233'
true
> null == undefined
true
> null === undefined
false
Js提供typeof()
来判断对象类型:
> typeof('233')
"string"
> typeof(233)
"number"
> typeof([2,3,3])
"object"
> typeof({'name':'finley'})
"object"
> typeof(null)
"object"
> typeof(undefined)
"undefined"
开始学习JS的朋友可以在浏览器开发者工具的console中使用JavaScript进行交互式操作,或者在html的<script>
元素中编写JavaScript脚本.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>First JavaScript</title>
</head>
<body>
<script>
var s = 'Hello World.';
document.write(s);
</script>
</body>
</html>
JavaScript 可以通过不同的方式来输出数据:
-
window.alert()
弹出对话框 -
document.write()
将内容书写到HTML文档中 -
console.log()
输出到浏览器控制台
JavaScript对象
JS使用var
关键字声明变量,其数据类型包括:
-
对象(Object)
-
空(Null)
-
未定义(Undefined)
布尔型取值只有两个,用关键字true
或false
表示。
示例(console):
> var a = 'hello'
> a
"hello"
> var b = "world"
> b
"world"
> var c = 1
> c
1
> var d = 1.0
> d
1
> var e = true
> e
true
> var f = null
> f
null
在Chrome的console下,可以不使用var关键字直接定义变量.
关键字null
表示变量为空值, undefined
表示值未定义.
Number
var x = new Number(123)
var x = 123
JS中只有一种数字类型, 可以带小数点也可以不带.数字不分为整数类型和浮点型类型,所有的数字都是浮点类型。JavaScript采用IEEE754标准定义的64位浮点格式表示数字.
当数字运算结果超过了JavaScript所能表示的数字上限,结果为正无穷大用关键字Infinity
表示, 或负无穷大-Infinity
.
NaN
表示非数字对象, isNaN(val)
可以检查一个对象是否为NaN.
示例:
> x = 1 / 'a'
NaN
> isNaN(x)
true
> x = 1 / 0
Infinity
> isNaN(x)
false
Array
var a = new Array(1, 2, 3, 4)
var a = [1, 2, 3, 4]
数组(Array)用于顺序存储元素,下标由0开始:
> a = [1,2,3]
[1, 2, 3]
> a[0]
1
> a[1] = "h"
"h"
> a
[1, "h", 3]
常用方法:
-
length
数组长度, 注意length是一个属性不是方法 -
indexOf(index)
返回某位置上的元素 -
slice(lower, upper)
数组切片, 返回[lower, upper)之间的元素. -
splice(start, len, item1, item2, ...)
修改数组,可以用于删除,插入元素 -
pop()
删除数组的最后一个元素并返回删除的元素 -
push(val)
向数组的末尾添加一个或更多元素,并返回新的长度 -
unshift(val)
向数组的开头添加一个或更多元素,并返回新的长度 -
toString()
把数组转换为字符串,并返回结果 -
sort(func)
对数组的元素进行排序
splice示例:
> a = [1, 2, 3, 4]
[1, 2, 3, 4]
> a.splice(0, 1) //删除从0开始1个元素, 返回被删除的内容
[1]
> a
[2, 3, 4]
> a.splice(0,2, "a", "b") //插入元素, 返回被替换的内容
[2, 3]
> a
["a", "b", 4]
sort示例:
> a = [2, 3, 1, 4]
[2, 3, 1, 4]
> a.sort()
[1, 2, 3, 4]
> a
[1, 2, 3, 4]
> a.sort(function(x,y){return y-x})
[4, 3, 2, 1]
> a
[4, 3, 2, 1]
String
var s = new String('hello')
var s = 'hello'
var s = "hello"
字符串字面值可以使用单引号对或双引号对包围.
String对象可以使用[]
访问元素.
常用方法:
-
length
长度 -
indexOf(pattern)
定位字符串中某子串首次出现的位置, 不存在返回-1 -
search(re)
正则表达式匹配 -
match(pattern)
函数用来查找字符串中特定的字符串,若存在则返回这个子串,否则返回null -
toUpperCase()
/toLowerCase()
大小写转换 -
replace(pattern, target)
替换子串 -
slice()
切片 -
substr(start,length)
返回start开始长为length的一段子串, 包含start.如果start为负数,则start=str.length+start;如果 length 为 0 ,负数或未指定,将返回一个空字符串。 -
substring(start,end)
返回指定位置[start, end)一段子串 -
spilt()
分割为数组
+
可以用于字符串连接, 甚至可以用于String与Number连接.
y="Hello "+"World";
"Hello World"
y = "Hello " + 233
"Hello 233"
Object
JavaScript中几乎一切都是对象.
>var user = {usernmame:'finley', password:'1234'}
>user
Object {usernmame: "finley", password: "1234"}
> user.usernmame
"finley"
对象也可以作为字典来使用:
> var user = {'username':'finley', 'password':'1234'}
> user['username']
> user['password'] = 'abcd'
成员可以动态添加:
> user.email = "finley@233.com"
"finley@233.com"
> user
Object {usernmame: "finley", password: "1234", email: "finley@233.com"}
JavaScript中函数也是对象, 同样可以作为成员.
> user.login = function() { return 'login'};
function() { return 'login'}
>user.login()
"login"
for-in循环可以遍历Object:
> for (item in user) {console.log(item)};
usernmame
password
email
login
更多关于对象的内容将在面向对象编程中介绍.
流程控制
if
示例:
if (time > 20) {
console.log('good evening')
}
else if (time > 12) {
console.log('good afternoon')
}
else {
console.log('good morning')
}
switch
示例:
var d = new Date().getDay();
switch (d) {
case 0:
x = "Sunday";
break;
case 1:
x = "Monday";
break;
case 2:
x = "Tuesday";
break;
case 3:
x = "Wednesday";
break;
case 4:
x = "Thursday";
break;
case 5:
x = "Friday";
break;
case 6:
x = "Saturday";
break;
}
for
for(var i = 1, s = 0; i < 101; i++) {
s += i
}
for-in
for-in循环可以用来遍历Array, Sting和Object.
遍历String时注意i为下标.
var arr = [1, 2, 3, 4, 5]
for (var i in arr) {
document.write(i + ' ')
}
var s = 'helloworld'
for (var i in s) { // i is index
document.write(s[i] + ' ')
}
var user = {usernmame:'finley', password:'1234'}
for (item in user) {
document.write(item + ' ')
};
while
var i = 0;
while (i < 5) {
i++;
}
document.write(i)
var i = 0;
do {
i++;
} while(i < 5);
document.write(i)
break / continue
break 语句用于跳出循环
continue 用于跳过循环中的一次迭代
函数与闭包
函数
function
关键字可以定义函数:
> function func(a) {return a;}
func(a) {return a;}
> func(1)
1
或者定义匿名函数:
> f = function (a) {return a;}
function(a) {return a;}
> f(1)
1
Js中函数也是对象, 拥有属性并可以进行拷贝和传递.
> typeof(f)
"function"
函数的参数传递和返回,均采用浅拷贝传递.
即只拷贝对象本身,成员中指向的其它对象不进行拷贝.
示例1:
> f = function (a) {a = 1}
function(a) {a = 1}
> a = 2
2
> f(a)
undefined
> a
2
示例2:
> f = function (a) {a[0]=1; a.n = 1}
function(a) {a[0]=1; a.n = 1}
> a = [2, 3, 4, 5]
[2, 3, 4, 5]
> f(a)
undefined
> a
[1, 3, 4, 5]
> a.n
1
函数可以嵌套定义, Js作用域是链式的内部作用域可以访问外部作用域的对象.
function outer() {
var tmp = 233;
function add() { tmp++; return tmp;}
return add;
}
> a = outer()
function add() { tmp++; return tmp;}
> a()
234
> a()
235
若参数未被赋值则为undefined
:
> f = function (a) {return a;}
function(a) {return a;}
> f()
undefined
可以用这个特性实现默认参数:
f = function (a) {
if (a === undefined) {
a = 0;
}
return a;
}
> f()
0
函数对象中的arguments
属性是一个保存了所有参数的数组.
function findMax() {
var i, m = 0;
for (i in arguments) {
if (arguments[i] > m) {
m = arguments[i];
}
}
return m;
}
> findMax(1,555,233,666)
666
这个示例也展示了如何使用可变参数.
因为函数也是对象这一特性, Js提供了call
和apply
两个函数进行调用.
> f = function(a,b) {return [a,b]}
> f.call(f, 1,2)
[1, 2]
> f.apply(f, [1,2])
[1, 2]
闭包
先看一个示例:
function outer() {
var tmp = 233;
function inner() { return tmp;}
return inner;
}
var c = outer();
document.write(c()); // c = 233
因为Js的链式作用域机制, 内部作用域可以访问外部作用域的对象, 但外部作用域无法访问内部作用域.
示例中通过返回的inner()
函数可以在外部访问outer()
的作用域.
inner()
函数就是outer()
函数的闭包.
闭包除了在外部访问作用域外另一个作用是使得局部变量始终保存在内存中.
将上述示例做一些修改:
function outer() {
var tmp = 233;
function add() { tmp++; return tmp;}
return add;
}
> a = outer()
function add() { tmp++; return tmp;}
> a()
234
> a()
235
可以看出局部变量tmp没有被回收.
面向对象
对象
JavaScript中没有类,但是其一切都是对象.
在数据类型一节中,我们把对象当做字典来认识, 介绍了对象的成员(属性和方法)以及它们的动态特性.
JavaScript支持new运算符:
function User(username, password) {
this.username = username;
this.password = password;
}
user = new User('finley', '1234');
document.write(user.username, user.password);
因为没有类, new运算符调用了相应的函数作为构造器.
同样因为没有类, 所以JavaScript无法使用构造函数创建类属性.
this
this是JavaScript关键字, 在函数调用时代表调用函数的对象.
对于全局性调用, this代表的全局对象Global.
var x = 1;
function func() {
this.x = 0;
}
func();
document.write(x); // output: 0
我们通常var
关键字声明变量, 但是直接给未声明的对象赋值一般也不会引起错误.
实际上,直接向未声明对象赋值等同于给this对象下添加并初始化同名成员:
x = 0; // 等价于this.x = 0;
对于对象方法调用, this代表代表调用的对象:
var obj = {};
obj.x = 1;
obj.func = function() {
this.x = 0;
};
obj.func();
document.write(obj.x); // output: 0
apply和call函数用于改变调用函数的对象, 即改变了函数调用的上下文:
var obj = {};
var another = {};
obj.x = 1;
another.x = 0;
obj.func = function() {
document.write(this.x)
};
obj.func.call(another); // output: 0
call和apply的区别在于, call使用可变参数传参, apply使用数组传参:
var obj = {};
obj.add = function(x, y) {
return x + y;
};
obj.add.call(obj, 1,1);
obj.add.apply(obj, [1,1]);
prototype
原型链系统是JS中代替传统继承系统的方案, 具有非常强的动态特性.
JavaScript中所有Object均拥有一个prototype属性, 将需要和派生类共享的成员写在prototype下, 私有的成员添加到Object中.
派生类继承基类的prototype属性, 构成链状结构称为原型链.
// 基类构造器
function User(username) {
this.username = username;
}
// 基类方法
User.prototype.hello = function() {
document.write('I am ' + this.username + '<br>');
};
// 派生类构造器
function Student(username, school) {
// this对象调用基类构造器, 进行基类构造
User.apply(this, arguments);
// 添加新增成员
this.school = school;
}
// 用基类prototype构造派生类的prototype
Student.prototype = Object.create(User.prototype);
//重写方法
Student.prototype.hello = function() {
document.write('I am ' + this.username + ' ,and I am a student.<br>');
};
// 新增方法
Student.prototype.enroll = function() {
document.write(this.username + ' enrolled in ' + this.school );
};
// 实例化
var user = new User('finley');
var student = new Student('finley', 'unknown');
// 测试
user.hello();
student.hello();
student.enroll();
上述示例展示了如何通过原型链继承并重写成员.
更多关于原型链的说明请参见:
模块
JavaScript的模块机制基于对象.
module.exports
属性指定了模块的公开对象,其它模块导入该模块时实际上是导入了这个对象.
require(path)
函数用于导入其它模块,参数指定要导入模块的路径,并返回目标模块的公开对象.
module.exports = config;
var config = require("./config.js");