Help:JavaScript
JavaScript,简称JS,是一门动态弱类型、基于原型、面向对象的脚本语言,作为开发Web页面的脚本语言而出名,但也被用到许多非浏览器环境中。JavaScript的标准是由ECMA国际制定的ECMAScript(简称ES),它规定了语法和语义,具有多个版本。目前,除Internet Explorer外,绝大多数浏览器都已支持ES6。
本页面介绍了一些JavaScript基础内容,建议有一定编程基础的用户阅读。
变量[编辑源代码]
JavaScript中变量(variable)分为全局(global)变量和局部(local)变量。全局变量可以在任何作用域被访问,而局部变量只能在自己的作用域内被访问。所有的全局变量都是globalThis对象中的属性,而在浏览器中globalThis就是window对象。
JavaScript使用var
、let
ES6关键字声明、初始化一个变量。声明时初始化是可选的,如果不初始化变量的值则为undefined。如果不使用关键字,且之前变量未被声明,则定义的变量一定是全局变量。在严格模式中,不能用这种方式定义全局变量,必须使用window.varName = value
来定义。定义已经声明的变量不需要再写关键字。
同一语句定义多个变量会从左往右依次初始化,而不是评估所有值再赋值。这一点与Lua和Python不同,而与某些通用结构化语言(如C、C#等)相似。想要像Lua和Python那样,可以使用数组解构。
var a = 1;
var b = 114, c = 514, d;
var e;
a = 2; // 定义已经声明的变量不需要再写关键字
var b = 1919; // 可以但没有必要
var c; // 不会改变c的值
z = "Global"; // 全局变量
"use strict";
window.variable = "RS"; // 可行
variable = "RS"; // 不可行
在全局作用域中,var
和let
声明的都是全局变量,而在局部作用域中都是局部变量。同一语句块内,var
声明的变量可以由var
再次声明,且值不会变化。而let
的声明既不能被重新声明,也不能用来重新声明已经声明的变量。
// 不行
var a = 1;
let a = 2;
// 也不行
let b = 1;
var b = 2;
// 更不行
let c = "JS";
let c = "CSS";
var
声明的局部变量都处于函数作用域内,而let
声明的局部变量处于其所在的最小语句块的作用域。for循环初始化语句声明的let
变量会持续到整个循环结束,而循环体内的let
变量只持续到一个循环周期结束。
function demo(c) {
if (c) {
var a = 114;
let b = 514;
}
// a为514,没有b
{
var x = 1919;
let y = 810;
{
var z = 114514;
}
}
// x为1919,z为114514,没有y
for (let i = 0; i < a; i++) {
var aa = i + 1;
let bb = i + 2;
// 第1次,i为0,aa为1,bb为2;第2次,i为1,aa为2,bb为3
}
// 运行后,没有i和bb,aa为115
}
另外,变量声明(带关键字)的值是undefined
,而变量定义(不带关键字)的值就是变量被赋予的值。利用该原理可实现多变量赋值。
// 括号一般省略不写
var a = (var b = 5); // a为undefined
var b;
var a = (b = 5); // a为5
// 可写作var a = b = 5;
其他赋值符[编辑源代码]
JavaScript有一些运算符作为运算和赋值的简略形式:
x += y |
等价于 | x = x + y
|
x -= y |
x = x - y
| |
x *= y |
x = x * y
| |
x /= y |
x = x / y
| |
…… |
此外,自增和自减运算符也可分别看作x += 1
和x -= 1
的二次简略形式。
常量ES6[编辑源代码]
常量(constant)声明是ES6新增的特性。常量通过const
关键字来声明,且声明时必须初始化。常量声明后不可更改其值,但如果其值为对象,更改对象属性是允许的。与变量类似,常量也有自己的作用域,其特点与let
类似。
const PI = math.pi;
pi = 114514; // 会报错
const a; // 不可行
const obj = {};
obj.a = 1; // 可以
obj = {a: 1} // 不可以
数据类型[编辑源代码]
JavaScript是动态弱类型语言,变量没有类型,值有类型,分为下面几种。变量类型可以通过typeof
运算符关键字取得,该一元运算符返回其后表达式的类型的字符串。
undefined[编辑源代码]
未定义类型,undefined
的类型。它不是对象,故不能获取或设置其属性。与字符串相加时它会隐式转化为"undefined"
,与数字相加时会得到NaN
。
number[编辑源代码]
数字类型。JavaScript不细分浮点、整形、长整形。数字类型的原型与Number.prototype
严格相等,但不是Number
类的实例。使用构造器new Number()
生成的数字是Number
类的实例,但属于对象类型。不要使用构造器,因为这会导致许多问题。
任何正数除以零得到正无穷Infinity
,任何负数除以零得到负无穷-Infinity
。0 / 0
、两个undefined
相加及undefined
与数字相加等不合法操作会得到NaN
(Not a Number)。它不等于任何值,包括其自身,也不大于或小于任何值,但它仍然属于数字类型。它具有传染性,与其它正常数字运算也会得到NaN
。因此涉及数字的运算需要留意代码运行过程中有无NaN
产生。不像Lua,这两者是可以用字面量获取的。
string[编辑源代码]
字符串类型,与许多语言类似,使用引号表达,字符串内使用相同引号需要加上一个反斜杠。JavaScript不区分“字符”和“字符串”。字符串类型的原型与String.prototype
严格相等,但不是String
类的实例。使用构造器new String()
生成的字符串是String
类的实例,但属于对象类型。不要使用构造器,因为这会导致许多问题。
转义序列[编辑源代码]
同其他语言类似,JS字符串有转义序列。如果在不可转义字符前加反斜杠,对该字符无影响。
转义序列 | |
---|---|
转义字符 | 意义 |
\' |
表示一个单引号 |
\" |
表示一个双引号 |
\\ |
表示一个反斜杠 |
\0 |
表示一个NULL字符 |
\b |
表示一个退格符 |
\r |
表示一个回车符 |
\v |
表示一个垂直制表符 |
\f |
表示一个换页符 |
\n |
表示一个换行符 |
\t |
表示一个制表符 |
\uXXXX |
表示一个Unicode字符,XXXX 为其十六进制编码
|
\xXX |
表示一个字节,XX 为其十六进制编码(00-FF)
|
\XXX |
表示一个字节,XXX 为其八进制编码(000-377)
|
模板字符串ES6[编辑源代码]
模板字符串是ES6新增的特性,使用字符`
(ASCII 96号字符,反引号)表示。字符串内可以用${val}
嵌入表达式的值。
"abc\n\td"
var name = 'RS\'s boxes';
var number = 2;
var templateString = `I have ${number.toString()} ${name}!`; // I have 2 RS's boxes!
// 表达式的值若非字符串类型会隐式转化为字符串,因此此处.toString()可省略
boolean[编辑源代码]
布尔值类型,包括真(true
)和假(false
)。比较运算符的返回值都是布尔值。当布尔值参与到数字运算中时,会转化为1或0。这一点需要尤其注意,因为JavaScript没有连比较语法糖,所以如果使用连比较可能得到意想不到的结果而又不会报错。
var a = 6;
console.log(7 > a > 5); // false(从左往右运算,7 > 6为true,转化为数字为1,1 > 5为假)
// 正确写法是7 > a && a > 5
与前面几种类似,不应当使用new Boolean
构造器。
function[编辑源代码]
函数类型。函数表示的是一系列命令组成的子程序。函数有参数和返回值,如果没有显式返回值则返回值为undefined
。返回一个值使用return
。函数返回值后,不再执行后面内容,因此应当将返回语句写在一个代码块的末尾。与Lua不同,JavaScript函数返回值只能有一个。[1]参数分为形参(parameters)和实参(arguments)。形参是函数定义时的参数,实参是实际调用时传入的参数。两者的个数不一定相等。参数是函数作用域中的局部变量。
定义一个函数的基本格式是:
// 具名函数
function func_name(param1, param2, ...) {
function_body
}
// 匿名函数
var func_xx = function(param1, param2, ...) {
function_body
}
注意:在局部作用域中用第一种方式只能将函数定义为局部变量。
函数调用的基本格式是:func(arg1, arg2, ...)
。这是一个表达式,解释它时会运行函数块中的代码,并得到其返回值。
arguments[编辑源代码]
arguments
是一个带值的关键字,它存储了(非箭头)函数调用时的所有实参。
arguments
类似于数组,可以对其进行索引以获得实参,并用length
属性获取其长度,但除此之外它没有任何别的数组属性。
在非严格模式之下,当形参没有默认参数、剩余参数和解构赋值时,arguments
和实参变量会始终保持一致,修改其中的一个则会影响另一个。反之,则不会相互影响。例如:
function demo(a, b) {
console.log(arguments[0], arguments[1], arguments[2], a, b);
arguments[0] = 114;
arguments[1] = 514;
arguments[2] = 1919;
console.log(arguments[0], arguments[1], arguments[2], a, b);
}
demo(810);
// 810 undefined undefined 810 undefined
// 114 514 1919 114 undefined
demo(8, 10);
// 8 10 undefined 8 10
// 114 514 1919 114 514
demo(8, 1, 0);
// 8 1 0 8 1
// 114 514 1919 114 514
function demo2(a, b) {
"use strict";
console.log(arguments[0], arguments[1], arguments[2], a, b);
arguments[0] = 114;
arguments[1] = 514;
arguments[2] = 1919;
console.log(arguments[0], arguments[1], arguments[2], a, b);
}
demo(810);
// 810 undefined undefined 810 undefined
// 114 514 1919 810 undefined
demo(8, 10);
// 8 10 undefined 8 10
// 114 514 1919 8 10
demo(8, 1, 0);
// 8 1 0 8 1
// 114 514 1919 8 1
一般地,不推荐更改arguments
的值。
在非严格模式下,可以用callee
和caller
获取当前函数本身(被调用函数)和调用本函数的函数(如果在全局作用域调用则为undefined
)。在严格模式下,访问这两个属性会报错。
this[编辑源代码]
this
是一个带值的关键字,它的值是(非箭头)函数作为对象属性被调用时(obj[key]()
或obj.method()
)对象(obj
)的值。同时,它也是构造器函数中实例本身的值。通常把作为对象属性的函数称为“方法”。
如果作为普通函数调用,this
的值为undefined
。在全局作用域中,当严格模式未启用,this
的值为window
,反之则为undefined
。
除了一般方式,还可以用函数的func.call(thisArg, arg1, arg2, ...)
和func.apply(thisArg, argsArray)
调用函数。在这两种方式中,需要用第一个参数指定this
的值。
var obj = {
method: function(val) {
this.prop = val
},
prop: 0
}
this.prop = 114514; // 等价于window.prop = 114514,严格模式报错
console.log(window.prop, obj.prop); // 114514 0
obj.method(114514); // 等价于obj.method.call(obj, 114514)或obj.method.apply(obj, [114514])
console.log(window.prop, obj.prop); // 114514 114514
var func = obj.method;
func(114514) // 相当于func.call(undefined, 114514)或func.apply(undefined, [114514])
// 会报错,因为this在调用时为undefined,不能对undefined定义属性
new关键字[编辑源代码]
new
运算符关键字可以添加到函数调用前,表示函数作为一个构造器被调用。解释构造器调用表达式时,首先会以函数的prototype
属性(任何一个非箭头函数创建时都会自动附上prototype,其中有一个constructor属性,其值为该函数本身)作为原型生成一个对象,然后这个对象会作为构造器函数的this
值运行构造器函数。构造器函数不应当有返回值,如果它的返回值不是对象,将不影响得到的对象,但如果返回是对象且不是构造出的对象,则构造出的对象会被返回的对象替换。
function A() {
this.b = 1;
return "114"
}
var a = new A();
console.log(typeof a); // object
console.log(a instanceof A) // true,因为new A()得到原型为A的实例
function B() {
this.c = 1919;
return new Error("你是一个一个")
}
var b = new B();
console.log(b instanceof B) // false,因为new B()得到一个Error对象
console.log(b instanceof Error) // true
有的函数不能用new
调用;有的函数只能用new
调用,如ES6类的构造器函数;有的两者都可以,如Number
,用new
调用返回对象,不用则返回数字类型。又如jQuery.Deferred()
,它用两种方法调用是等价的,因为它会检查this
的值是不是自身的实例,不是则会重新用new
调用自身。
箭头函数[编辑源代码]
ES6中新增了箭头函数,作为匿名函数的一个简略形式。其基本语法为(param1,param2) => expr
或(param1, param2) => { function_body }
。第一种形式相当于(param1, param2) => { return expr }
。第一种形式中,如果expr是一个对象字面量,这个对象必须用圆括号包裹避免被解释为第二种形式。如果有且只有一个形参,那么参数列表两侧的括号可以省略。
var f = a => {a: a};
var g = a => ({a: a});
console.log(f(1)); // undefined,因为花括号被理解为语句块
console.log(g(1)); // {a: 1}
// 相当于
var func = (a) => {
return {a: a};
}
箭头函数没有自己的this
和arguments
。它们两个的值为箭头函数被定义的作用域中的值。
var a;
var o = {
getArrow() {
return () => {
this.a = 1;
a = arguments
}
}
}
o.getArrow()(114);
console.log(o.a, a[0]); // 1 114
与上面几种数据类型不同,函数构造器构造的对象属于函数类型,且函数类型都是函数类的实例。函数构造的语法为new Function(code_string, arg1name, arg2name, ...)
,构造出的函数可用.call()
和.apply()
方法调用。但是,通常使用字符串变量构造函数是危险的,因为输入的字符串是未知的,因此建议不要使用。
symbol[编辑源代码]
object[编辑源代码]
对象类型。JavaScript中几乎所有值都是对象,但不一定是对象类型。凡非上面几种类型的对象皆属于对象类型。
- ↑ Python也只有一个,之所以看到返回语句中有几个逗号隔开的值是因为返回了一个元组,这个元组可以进行拆包,于是看起来就非常像多个。