javascript基础语法学习

前言

这是一次简单的、对于JavaScript基础语法的重新学习,只记录JavaScriptC\C++的不同之处,相同的不再赘述。

其中加入了一些ES6的特性。

一、输出

  • 使用 window.alert() 弹出警告框。

  • 使用 document.write() 方法将内容写到 HTML 文档中。

  • 使用 console.log() 写入到浏览器的控制台。(常用)

二、变量

1.声明

  • var namevar num = 1。 不用说明变量的类型,编译器会自行判断。若初始化,则是初始化值的类型;若未初始化,则是undefined数据类型。每当该变量再次被赋值或再次被声明定义时,会被转换成新的数据类型。

  • let name。 声明的是块级作用域变量,只在块级作用域内有效,不能被块外部访问,且不可重名。块级指{….}之内的内容,如for{}中。

  • const name = 'jek'。 声明常量,块级作用域,必须初始化,不可修改。

letconst两种声明方式,和块级作用域ES6特性。

练习代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/**
* var、let、const声明变量
*/

// var声明变量后,可以赋不同类型的值和重新声明定义
var v1 = 1;
console.log(v1, typeof v1); // 1 number
v1 = 'ssss';
console.log(v1, typeof v1); // ssss string
var v1 = false;
console.log(v1, typeof v1); // false boolean

// var声明函数内局部变量,外部可访问,但不能访问到局部变量的值。按理来说外部是不可访问的,此处我也很迷惑
function myFunction() {
var name = 'bbbb';
}
console.log(name, typeof name); // string

// var声明块级变量,外部可以访问且值相同
for (var i = 0; i < 5; i++) {
var x = i;
}
console.log(x); // 4

// let 声明块内变量,块级作用域,块内有效,外部无法访问
for (var i = 0; i < 5; i++) {
let y = i;
}
// console.log(y); // ReferenceError: y is not defined

// const 声明,无法修改
const pi = 3.14;
// pi = 3; // TypeError: invalid assignment to const `pi'
console.log(pi);

// const 声明,块级作用域,块内有效,外部不可访问
for (var i = 0; i < 5; i++) {
const z = i;
}
// console.log(z); // ReferenceError: z is not defined

输出结果:

1
2
3
4
5
6
1 "number"
ssss string
false "boolean"
string
4
Uncaught ReferenceError: y is not defined

(注:js有变量提升的特性,即允许先使用(如赋值)再声明(必须用var声明),但不建议使用)

2.数据类型(6种值类型+3种引用类型)/(6种原始类型+对象类型)

typeof(var)函数检测var变量的变量类型。

1)string

字符串:var str = 'hello'。 可以用双引号或单引号包裹,如"hello"'hello'都可以(建议用单引号)。可以通过下标访问字符串中的字符。

两个字符串之间可以通过 + 拼接。

ES6中新增了字符串模板的特性。字符串需用 `` 包裹起来,字符串中需要替换的变量用${}包裹。示例:

1
2
var name="lsj";
console.log(`hello ${name}`);

输出hello lsj

2)number

数字:var num = 1。 包括整数、浮点数、科学计数法表示的数(2.1e10)。

js中浮点数的精度是个待解决的问题。

数字可以与字符串通过 + 拼接,此时数字会被强转成字符串。

3)boolean

布尔类型:var bo = true

4)undefined

无定义类型:var name。 当变量声明却没有被定义,或者输出一个未声明的变量时,则为undefined类型。

5)null

空类型:var name = nullname = null。 用来将变量置空,其实表示一个空对象引用。

6)symbol

symbolES6引入了一种新的原始数据类型,表示独一无二的值。

Symbol()函数栈不能用 new 命令,因为 Symbol 是原始数据类型,不是对象。可以接受一个字符串作为参数,为新创建的 Symbol 提供描述,用来显示在控制台或者作为字符串的时候使用,便于区分。

1
2
3
4
5
6
7
let sy = Symbol("KK");
console.log(sy); // Symbol(KK)
typeof(sy); // "symbol"

// 相同参数 Symbol() 返回的值不相等
let sy1 = Symbol("kk");
sy === sy1; // false
7)array

数组:var arr = [1,2,3]或者var arr = new Array(1,2,3)。 在js中数组类型其实算作一种对象类型。

同时,js数组与C数组还有几个不同点:

  • js数组的每一项可以用来保存任何类型的数据,也就是说,可以用数组的第一个位置来保存字符串,第二个位置保存数值,第三个位置保存对象。
  • 数组的大小是可以动态调整的。
  • 访问超限时,输出undefined
8)function

函数。在js中,函数也是变量,后面详细介绍。

9)object

对象。也算变量,后面再详细介绍。

练习代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
/**
* 变量数据类型
*/

// number类型。包括浮点数、整数
var num = 3;
console.log(num, typeof num);

// string类型。定义时,可以用单引号,也可以用双引号
var str = 'hello';
console.log(str, typeof str);

// 获取字符串中的某个字符
console.log(str[1], typeof str[1]);

// 字符串是不可变的,如果对字符串的某个索引赋值,不会有任何错误,但是,也没有任何效果
str[1] = 'b';
console.log(str);

// 拼接字符串和数字
console.log(str + num);

// boolean类型
var bo = true;
console.log(bo, typeof bo);

// undefined类型
var udf;
console.log(udf, typeof udf);

// null类型
var nu = null;
console.log(nu, typeof nu);

// array类型
var arr = [1,2,3];
// var arr = new Array(1,2,3);
console.log(arr, typeof arr);

// 访问超限,会输出undefined
console.log(arr[9]);

// 数组中可包含任意类型
arr = [1, 'hello', [2, 3]];
console.log(arr, typeof arr);

// 使用push()函数可向数组末尾添加元素
// arr.push('new');
// console.log(arr);
// 为什么要用下面这种方法呢?异步问题!!!上下两端代码输出不一样!!!
setTimeout(() => {
arr.push('new');
console.log(arr);
}, 3000);

// function类型
var fun = function() {
console.log('this is a function');
}
console.log(fun, typeof fun);

// object类型
var obj = {
name: 'tomcat',
getName: function() {
return name;
}
};
console.log(obj, typeof obj);

输出结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
3 "number"
hello string
hello 3
true "boolean"
undefined "undefined"
null "object"
[1, 2, 3] "object"
undefined
[1, "hello", Array(2)] "object"
ƒ () {
console.log('this is a function');
} "function"
{name: "tomcat", getName: ƒ} "object"

三、运算符

特别注意:在js中有相等==和绝对相等===,不相等!=和不绝对相等!==。区分如下:

  • ==。“值”相等为true,类型可以不相等。如:1 == ‘1’
  • ===。“值”和类型都必须相等才为true
  • !=。类型相等时,“值”不相等才为false
  • !==。“值”或者类型不相等,为false
1
2
3
4
5
6
7
8
9
10
var num1 = 1;
var str1 = "1";

// == 与 ===
console.log(num1 == str1); // true
console.log(num1 === str1); // false

// != 与 !==
console.log(num1 != str1); // false
console.log(num1 !== str1); // true

推荐使用===!==

其它运算符与c语言的相同。

四、语句

if、for、while、switch等,与c相同。

ES6新特性:运算符 ... 用于取出参数对象/数组的所有可遍历属性,然后拷贝到当前对象。如:

1
2
3
4
5
6
7
let person = {name: "Amy", age: 15};
let someone = { ...person };
console.log(someone); //{name: "Amy", age: 15}

let arr = [1, 2];
let arr1 = [...arr];
console.log(arr1); // [1, 2]

五、函数

1.函数定义

1)函数语句定义(常规)

参数,可有可无,不用指定数据类型(js中,变量本就不需指定类型),默认为undefined类型。传入参数个数不定,按顺序未传入的参数为undefined。ES6中参数可以设定默认值

返回值,可有可无。

1
2
3
4
5
6
7
8
9
10
11
12
13
function funA (a, b) {
console.log('a is ' + a);
console.log('b is ' + b);
console.log('a+b is ' + (a+b));
}
funA(2, 4);
funA(2);

// ES6 函数参数可以设定默认值
function funB (a, b = 10) {
console.log('默认值 || ' + (a+b));
}
funB(3);

输出:

1
2
3
4
5
6
7
a is 2
b is 4
a+b is 6
a is 2
b is undefined
a+b is NaN
默认值 || 13
2)函数表达式(函数变量)定义

将匿名函数赋给一个变量,该变量可以跟函数一样使用,其数据类型为函数类型。

1
2
3
4
var vB = function (a, b) {
return a*b;
};
vB(2, 3);

在《JavaScript权威指南》中说:

以表达式定义的函数只适用于它作为一个大的表达式的一部分。

其实,我觉得表达式定义形式也可以用于一般函数。感觉两者没什么太大区别,可能也是我不了解js的内部实现。

2.匿名函数

不带函数名的函数,即为匿名函数,如:

1
2
3
function () {
console.log('this is an anonymous function');
}

可单单定义一个匿名函数是无法调用的,因为它没有名字。但,我们可以通过将匿名函数赋给变量(该变量成为函数类型变量),或函数自调用的方式,来调用匿名函数。

ES6新增用箭头函数来表示匿名函数:

1
2
3
4
5
// ES6 新增箭头函数来表示匿名函数,一个参数时可以省略圆括号,一条语句可以省略花括号
var vB = (a, b) => {
console.log('箭头函数 || a-b is ' + (a-b));
};
vB(6, 3);

3.函数自调用

其形式为 (...匿名函数定义...)(...参数...); 。作用是在函数定义后立即调用该函数:

1
2
3
(function (a, b) {
console.log('函数自调用 || a-b is ' + (a-b));
})(6, 2);

1
2
3
((a, b) => {
console.log('箭头函数 || a-b is ' + (a-b));
})(6, 2);

输出:

1
2
函数自调用 || a-b is 4
箭头函数 || a-b is 4

4.函数作为值

在js中,函数也是变量,那么函数就可以当作一个变量来进行传递。

1)函数作为返回值(函数闭包)

函数返回一个子函数(嵌套函数),那接受这个返回值的变量即是函数类型变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function fa (first) {
return function (second) {
return first += second;
};
}
var son = fa(0); // son变量接受一个返回函数
/*相当于
var son = function (second) {
return first += second
};
只是这样定义时,不能使用函数fa()的局部变量first
*/
console.log(son(1)); // 输出 1
console.log(son(1)); // 输出 2

1
2
3
4
5
6
7
8
function fa (first) {
return (second) => {
return first += second;
};
}
var son = fa(0);
console.log(son(1));
console.log(son(1));

1
2
3
4
5
6
7
8
// 直接将fa函数写成自调用的匿名函数,自调用后返回一个函数给son变量
var son = (((first) => {
return (second) => {
return first += second;
};
})(0));
console.log(son(1));
console.log(son(1));

输出:

1
2
1
2

从第一个代码示例中,我们可以发现:返回一个函数(fa)的内部函数(son)时,我们可以通过这个返回的函数(son)来使用外函数(fa)中的局部变量。而且在不断调用内部函数(son)的过程中,外函数(fa)并没有被销毁(出栈)。由于这种机制,我们就可以让函数拥有私有变量,不能被全局使用,但又可以一直保持值不被销毁。而这就是闭包

代码:

1
2
3
4
5
6
7
8
9
//函数闭包。使函数可以拥有私有变量(外部不可访问,同时也不会因为函数调用而刷新)
var add = (() => {
var counter = 0;
return () => {return counter += 1;}
})();
add(); // add()实际调用的是上述匿名主函数中返回的匿名子函数
add();
add();
console.log('函数闭包 || ', add());

输出:

1
函数闭包 ||  4
2)函数作为参数(多态性/Lambda表达式)

将一个自定义函数作为参数传入另一个函数中,这个传入函数可以根据不同情况自行定义,从而让被传入函数实现不同效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 函数作为参数。使多态性得以体现
function appendDiv (callback) {
for(var i=0;i<10;i++){
var div = document.createElement('div');
div.innerHTML = i;
document.body.appendChild(div);
// 如果传入的参数是一个函数类型的变量
if(typeof callback == 'function'){
callback(div); // 将该形参作为函数变量使用
console.log(callback);
}
}
}
// 将一个自定义的匿名函数作为参数传入appendDiv()函数
appendDiv((node) => {
node.style.display='block'; // 让该元素以块元素展示(独占一行)
});
appendDiv((node) => {
node.style.display='inline'; // 让该元素以行内元素展示
});

显示结果:
函数作为参数

函数作为参数传入另一个函数的方式,常用于将自定义的回调函数传入接口函数中。但,在传入自定义参数到接口函数的这种场景中,常常是将回调函数作为对象中的一个方法传入的。

如微信小程序开发中常用的网络请求接口函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
wx.request({
url: wx.getStorageSync('requestUrl') + '/small/config',
method: 'GET',
data: {
},
header: {
'content-type': 'application/json'
},
success: (res) => {
console.log('get config: ', res.data.WeCosUrl)
wx.setStorageSync('wecosUrl', res.data.WeCosUrl)
}
});

wx.request()是微信提供的用于网络请求的接口函数,其参数是一个对象。对象中的success方法,是wx.request()函数执行访问成功后调用的函数,由用户自定义。而这就是一种多态性。

六、对象

1. Js 中的对象

在js中,大部分事物都是一个对象。引用《JavaScript权威指南》中的话说:

除了字符串、数字、true、false、null和undefined之外,JavaScript中的值都是对象。尽管字符串、数组和布尔值不是对象,但它们的行为和不可变对象非常类似。

字符串有对象的部分特性,如:

1
2
var message='Hello World!';
console.log(message.length); // 获取字符串的长度属性

但,字符串“对象”的属性是不可修改的,如:修改message.length的大小是无效的。同时,字符串还有很多常用的自带方法,如:内容匹配match()、内容替换replace()、转换大小写toUpperCase()/toLowerCase()等等。

2. 对象创建

js中,对象用{}将属性和方法包裹起来。每个属性和方法都有唯一的名字,而且名字和值之间是通过:一一映射的。如:

1
2
3
4
5
6
7
var obj = {
name: 'tomcat',
getName: function() {
return this.name;
}
};
console.log(obj.getName()); // 输出 tomcat

this的注意点

this关键字指向的是调用该函数的对象。如果不使用this.name,可能会返回全局中的name变量。如:

1
2
3
4
5
6
7
8
9
10
11
12
var obj = {
name: 'tomcat',
getName: function() {
return name;
},
// ES6新特性:方法名简写
setName(name) {
this.name = name;
}
};
var name = 'tom';
console.log(obj.getName()); // 输出 tom

注意:

另外,箭头函数虽然是匿名函数的一种简写,但实际上,箭头函数和匿名函数有个明显的区别:this指针的指向。匿名函数中this指向调用者对象,而箭头函数中的this指向上一层调用者对象。如:

1
2
3
4
5
6
7
8
9
10
var name = 'tom';
var obj = {
name: 'tomcat',
getName: () => {
console.log(this); // 输出 window对象
// 由于箭头函数,this指针指向上一层对象,即全局对象,因此这里的this.name是全局中的name变量
return this.name;
}
};
console.log(obj.getName()); // 输出 tom

所以,在对象的方法定义中,不建议使用箭头函数


对象还可以通过new + 构造函数的方法来创建,如:

1
var o = new Object(); // Object()是Object这个js“原型”对象的构造函数。返回一个空对象

3. Js 对象属性和方法的动态性

可以动态增加与删除对象中的属性和方法,如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 动态增加属性 方法
var book = {};
// var book = new Object();
book.author = 'es6';
book.title = 'JavaScript';
book.changeTitle = function () {
this.title = 'python';
return this.title;
};
console.log(book.author, book.title, book.changeTitle()); // 输出 es6 JavaScript python

// 删除属性、方法
delete book.author;
console.log(book.author); // 输出 undefined

4. Js 对象的引用与赋值

js中对象值是引用类型,相当于指针。

1
2
3
4
5
6
7
// 对象的引用与赋值
var obj1 = {
  a : 1
}
var obj2 = obj1;
obj2.a = 2;
console.log(obj1.a); // 输出2

var obj2 = obj1;相当于把obj1的指针赋给obj2,所以obj1obj2指向同一对象的数据。

同时,若函数参数为对象,并在函数中修改对象中数据,那也会影响到函数外的对象。

那js应该如何正确的进行对象赋值呢,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 对象间赋值的正确方法
var obj3 = {
a:1,
b:2,
c:3,
d:[0,1,2,3]
};
var jsonStr = JSON.stringify(obj3);
var obj4 = JSON.parse(jsonStr);
obj4["e"] = 4;
obj4["d"][0] = 11;
console.log(obj3); //Object {a: 1, b: 2, c: 3, d: [0,1,2,3]}
console.log(obj4); //Object {a: 1, b: 2, c: 3, d: [11,1,2,3], e: 4}

七、模块的导入导出

ES6新特性。解决不同Js文件之间,如何进行变量共享的问题。

1. export

export声明导出的变量,如:

1
2
3
4
5
6
7
8
9
10
//--------exportTest.js----------
let myName = "Tom";
let myAge = 20;
let myfn = function(){
return "My name is" + myName + "! I'm '" + myAge + "years old."
}
let myClass = class myClass {
static a = "yeah!";
}
export { myName, myAge, myfn, myClass }

2. import

import从指定js文件导入变量,如:

1
2
3
4
5
6
7
//--------importTest.js----------
import {myName, myAge, myfn, myClass} from "./exportTest";

console.log(myfn()); // My name is Tom! I'm 20 years old.
console.log(myAge); // 20
console.log(myName); // Tom
console.log(myClass.a );// yeah!
  • 可以用as关键字重新命名导入变量,如:import import { myName as name1 } from 'xxx.js'。将导入的myName变量重命名为name1,在当前文件中使用。

  • 导入变量可以使用但不能进行修改。

3. export default

  • 在一个文件或模块中,export 可以有多个,export default 仅可以有一个。

  • export default 中的 default 是对应的导出接口变量。

  • 通过 export 方式导出,在导入时要加{ }export default 则不需要。

  • export default 向外暴露的成员,可以使用任意变量来接收。

1
2
3
//--------exportTest.js----------
let myName = "Tom";
export default myName
1
2
3
4
//--------importTest.js----------
import name from "./exportTest";

console.log(name); // Tom

参考