例如运行当前目录下的 example.js 文件,可以这样写。
$ node ./example.js
$ node ./fun-dance.js
直接在命令行执行 node
命令,进入 REPL
(Read–eval–print loop,"读取-求值-输出"循环) 环境。
$ node
> 1 + 2
> 3
熟悉 Java、C 和 Perl 这些语言的开发者会发现 JavaScript 的语法很容易掌握,因为它借鉴了这些语言的优秀语法。
/*
),以星号和单斜杠结尾(*/
)关键字列表:break、case、catch、continue、default、delete、do、else、finally、for ...
保留字列表:abstract、boolean、byte、char、class、const、debugger、double、enum、export、extends、final、float、goto、implements、import ...
变量声明
var test = 'hi';
var test = 'hi', age = 25;
JavaScript 是弱类型的,所以无需明确的类型声明。
var num = 25;
num = true; //更改为bool类型
基本类型的变量中存储的是简单的数据段,而引用类型变量存储的是指向对象的引用
定义
var num = 8.6;
JavaScript 中 number 其实都是浮点数,那么问题来了
0.3 === 0.1 + 0.2;
常用方法:toFixed()、toString() 更多方法参考
定义
var str = 'abc123';
字符串拼接:
str + 'b';
常用方法 : indexOf() 、replace() 、split() ... 更多方法见
用于表示尚未存在的对象
var obj = null;
任何没有被赋值的变量都有 undefined 值
var a;
typeof a;
var b = {};
b.xx;
有两个值,true 和 false
var a = false;
Object 是一个属性的集合,键值对
所有对象都由 Object 继承而来,Object 对象中的所有属性和方法都会出现在其他对象中
常用属性/方法:
var a = [];
var b = ['facebook', 'google'];
var a = ['xx', 'yy'];
a.forEach(function(item, index) {
console.log(item, index);
});
join([separator = ','])
var a1 = ['a', 'b'];
a1.join('|');
push(element1, ..., elementN)
var a1 = ['a', 'b'];
var a2 = a1.push('c');
a1;
a2;
var d = new Date();
d.getFullYear();
设置、获取时间方法
/pattern/flags //直接量语法
new RegExp(pattern[, flags]) //创建 RegExp 对象的语法
var a = /\d/g;
var b = new RegExp('\\d', 'g');
Boolean 值、数字和字符串的原始值都是伪对象,对象就都继承了 Object 的 toString() 方法
var a = false;
a.toString();
var b = 10;
b.toString(2);
[1, 2].toString();
parseInt('0xA');
parseInt('a1', 16);
parseInt('a1');
parseInt('1a');
0位是数字,查到非数字为止,否则返回 NaN
参考:http://www.w3school.com.cn/js/pro_js_typeconversion.asp
思考以下结果
'5' == 5;
'5' === 5;
===、!== ,不会做类型转换, 约定等性判断必须用全等/非全等号
只要最终的结果已经可以确定是真或假,求值过程便终止,两种短路场景
true || anything;
false && anything;
false && console.log(1);
if (i > 30) {
console.log('大于 30');
} else if (i < 0) {
console.log('小于 0');
} else {
console.log('在 0 到 30 之间');
}
switch 语句是 if 语句的兄弟语句。
switch (i) {
case 20:
console.log('20');
break;
case 30:
console.log('30');
break;
default:
console.log('other');
break;
}
do-while 语句。 do-while 语句是后测试循环,即退出条件在执行循环内部的代码之后计算。这意味着在计算表达式之前,至少会执行循环主体一次。
var i = 0;
do {
i += 2;
} while (i < 10);
while 语句。 while 语句是前测试循环。这意味着退出条件是在执行循环内部的代码之前计算的。因此,循环主体可能根本不被执行。
var i = 0;
while (i < 10) {
i += 2;
}
for 语句是前测试循环,而且在进入循环之前,能够初始化变量,并定义循环后要执行的代码。
var iCount = 6;
for (var i = 0; i < iCount; i++) {
console.log(i);
}
for-in 语句是严格的迭代语句,用于枚举对象的属性。
for (var i in global) {
console.log(i);
}
break 和 continue 语句。 它们的不同之处:break 语句可以立即退出循环,阻止再次反复执行任何代码。而 continue 语句只是退出当前循环,根据控制表达式还允许继续进行下一次循环。
var iNum = 0;
for (var i = 1; i < 10; i++) {
if (i % 5 === 0) {
//break; //输出什么?
//continue; //输出什么?
}
iNum++;
}
console.log(iNum);
函数在 JavaScript 非常重要
函数是由这样的方式进行声明的:关键字 function、函数名、一组参数,以及置于括号中的待执行代码。
function say(name, msg) {
console.log('Hello ' + name + msg);
}
say('David', ' Nice to meet you!') //调用函数
//函数直接量
var sayHi = function(){ };
sayHi();
在 JavaScript 中无论是基本类型还是引用类型,函数参数都是按值传递的,先看基本类型:
function test(num) {
num += 10;
return num;
}
var num = 10;
var res = test(num);
console.log(num);
console.log(res);
再看引用类型:
function setName(obj) {
obj.name = 'facebook';
}
var company = {
name: 'google'
};
setName(company);
console.log(company.name);
总的来说,基本类型的参数复制的是具体的值,而引用类型的参数复制的是这个对象的引用。
function fn(arg, callback) {
var num = arg * arg;
callback(num);
}
fn(2, function(result) {
console.log(result);
});
函数如何返回值
function sum(num1, num2) {
return num1 + num2;
}
函数只需要使用 return 运算符后跟要返回的值即可。 与在 Java 中一样,函数在执行过 return 语句后立即停止代码。因此,return 语句后的代码都不会被执行。
函数默认返回值是什么?
我们会通过以下几个个方面来讨论 JavaScript 高级特性
以上7个方面,前面是理解后面的基础,在构造函数和继承理解遇到困惑时请回顾前面两个概念来帮助理解。
首先要有个对象
var jane = {
name: 'Jane',
describe: function() {
return 'Person named ' + this.name;
}
};
jane.name
jane.describe
jane.describe();
jane.name = 'John';
"[]"操作符让我们可以使用表达式来使用对象的属性和方法
obj['some' + 'Property'] || obj[propKey]; //表达式 || 变量
操作符内部强制转换成字符串
obj[3 + 3]; // key: 强制转换为 '6'
调用对象方法
obj['myMethod']();
设置属性或方法
obj['anotherProperty'] = 'def';
两种删除属性或方法的方式,如果我们删掉一个对象属性,那么它将在对象中彻底删除。
delete obj.hello;
delete obj['hello'];
function Company(name) {
this.name = name;
}
Company.prototype.showCompany = function() {
console.log(this.name);
};
var google = new Company('谷歌');
google.showName(); //输出什么?
this 是函数的隐含参数`,在 JavaScript 中,this 指向函数执行时的当前对象。
function bankCard() {
'use strict';
return this;
}
bankCard() === undefined
var obj = {
method: bankCard
};
obj.method() === obj
var work = {
money: 9999,
goodJob: function() {
return ++this.money;
}
};
var func = work.goodJob;
func();
work.goodJob();
怎么会这样?
这里很容易混淆,在调用 func() 时,this 已经指向 global 对象了
var nameObj = {
name: 'Tom',
showName: function() {
console.log(this.name);
},
waitShowName: function() {
this.showName();
setTimeout(function() {
this.showName();
}, 1000);
}
};
nameObj.waitShowName();
约定上,我们会使用 self 暂存 this
var nameObj = {
name: 'Tom',
showName: function() {
console.log(this.name);
},
waitShowName: function() {
var self = this;
this.showName();
setTimeout(function() {
self.showName();
}, 1000);
}
};
nameObj.waitShowName();
每个 JavaScript 函数都有 prototype 属性,这个属性引用了一个对象,这个对象就是原型对象。原型对象初始化的时候是空的,我们可以在里面自定义任何属性和方法,这些方法和属性都将被该构造函数所创建的对象继承。
实例就是通过构造函数创建的。实例一创造出来就具有 constructor 属性(指向构造函数)和 _proto_ 属性(指向原型对象),
构造函数中有一个 prototype 属性,这个属性是一个指针,指向它的原型对象。
原型对象内部也有一个指针(constructor 属性)指向构造函数。
实例可以访问原型对象上定义的属性和方法。
function Company(name) {
this.name = name;
}
Company.prototype.showCompany = function() {
console.log(this.name);
};
var google = new Company('谷歌');
google.showCompany();
function Company(name) {
this.name = name;
}
Company.prototype.showCompany = function() {
console.log(this.name);
};
function Google(name) {
this.name = name;
}
Google.prototype = new Company();
Google.prototype.constructor = Google;
Google.prototype.showCompany = function() {
console.log('巨头公司' + this.name);
};
var google = new Google('谷歌');
google.showCompany();
var facebook = new Company('脸谱');
facebook.showCompany();
在学习 JavaScript 的变量作用域之前,我们应当明确几点:
JavaScript 的变量作用域是基于作用域链的。
JavaScript 没有块级作用域。
var a = '0';
function level0() {
var a = '1';
function level1() {
var a = '2';
function level2a() {
console.log(a);
}
console.log(a);
level2a();
}
console.log(a);
level1();
}
level0();
JavaScript 的作用域链。
函数体内部,局部变量的优先级比同名的全局变量高。
JavaScript 没有块级作用域。
未使用var
关键字定义的变量都是全局变量。
全局变量都是全局对象的属性。
闭包(closure)是 JavaScript 语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现。
闭包 是指有权访问另一个函数作用域中的变量的函数
创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量
一般函数执行完毕后,局部活动对象就被销毁,内存中仅仅保存全局作用域。使用闭包可以把局部变量驻留在内存中,成为封装的局部私有变量。
function getStaffNum() {
var staffNum = 5000;
return function() {
staffNum++;
console.log('Num is ' + staffNum);
}
}
var staffNum = getStaffNum();
staffNum(); // 输出什么?
staffNum(); // 输出什么?
Node.js 采用模块化结构,遵照 CommonJS规范的定义和使用。模块与文件是一一对应的。加载一个模块,实际上就是加载对应的一个模块文件。
require 方法用于指定加载模块。
var a = require('a');
上面代码表明,以当前目录下的a.js
文件为路径,加载名为a
的模块。由于 require
方法默认加载后缀为.js
的文件,因此可以把后缀省略,所以也可以直接如下写法。
var a = require('./a');
Node.js 系统提供了一系列功能模块,与操作系统交互与调用。这些核心的功能模块集成在 node 中,可以直接使用。
下面是一个最简单的模块,新建一个a.js
文件如下:
module.exports = function(i) {
console.log(i);
};
上面代码就是一个模块,它通过module.exports
变量,对外输出一个方法。
使用方法如下:
var m = require('./a');
m('输出');
常用几个全局对象
module.exports
属性是该模块对外的接口,被 require 方法调用。module.exports
属性,是该属性的简写形式。exports 也不是全局变量,而是每个模块私有的。Node 提供一些全局函数。
最常用的捕获异常的方式,就是使用try...catch
。但是却无法捕获异步运行的代码抛出的异常。
try {
setTimeout(function() {
throw new Error('error');
}, 16);
} catch(err) {
console.log(err);
}
代码抛出的异常无法被 catch 代码块捕获。
怎么办?
当一个异常未被捕获,就会触发 uncaughtException
事件,可以对这个事件注册回调函数,从而捕获异常。
process.on('uncaughtException', function(err) {
console.error('Error caught in uncaughtException event:', err);
});
try {
setTimeout(function() {
throw new Error("error");
}, 16);
} catch(err) {
console.log(err);
}