注:本文所有知识点总结或摘抄自廖雪峰javascript教程,点击查看更详细的讲解。
1.javascript简介
-
ECMAScript
是一种语言标准,而JavaScript
是网景公司对ECMAScript
标准的一种实现。可以说javascript
语言的标准是ECMAScript
。
2. 入门基础
- 在编写JavaScript代码时,经常需要在
Console
运行测试代码,常用console.log()
。 - 对于复杂的层级嵌套,需要把部分代码抽出来,作为函数调用,以减少代码的复杂程度。
- JavaScript严格区分大小写,如果弄错了大小写,程序将报错或者运行不正常。
- JavaScript不区分整数和浮点数,统一用
Number
表示。 - JavaScript有两种比较运算符:
第一种是==
比较,它会自动转换数据类型再比较,很多时候,会得到非常诡异的结果;
第二种是===
比较,它不会自动转换数据类型,如果数据类型不一致,返回false,如果一致,再比较。 -
NaN
这个特殊的Number
与所有其他值都不相等,包括它自己:
NaN === NaN; // false
唯一能判断NaN
的方法是通过isNaN()
函数:
isNaN(NaN); // true
面试题中经常出现null
undefined
NaN
相关知识点。
- 要比较两个浮点数是否相等,只能计算它们之差的绝对值,看是否小于某个阈值:
Math.abs(1 / 3 - (1 - 2 / 3)) < 0.0000001; // true
1 / 3 === (1 - 2 / 3); // false
-
null
表示一个“空”的值,它和0
以及空字符串' '
不同,0
是一个数值,''
表示长度为0的字符串,而null
表示“空”。还有一个和null
类似的undefined
,它表示“未定义”。 - 数组的元素可以通过索引来访问,索引的起始值为0。
- 对象是一组由键(字符串类型)-值(任意数据类型)组成的无序集合,每个键又称为对象的属性。
- 申明一个变量用
var
语句,变量名是大小写英文、数字、$和_的组合,不能用数字开头,不能是关键字。 - 变量本身的数据类型不固定的语言称之为动态语言,与之对应的是静态语言。
- 变量没有通过
var
申明就被使用,那么该变量就自动被申明为全局变量。特别需要注意i
的声明。 - 启用
strict
模式的方法是在JavaScript代码的第一行写上:
'use strict';
- ES6新增了一种模板字符串代替加号:
var name = '小明';
var age = 20;
var message = `你好, ${name}, 你今年${age}岁了!`;
alert(message);
- 常用的操作字符串方法
调用这些方法本身不会改变原有字符串的内容,而是返回一个新字符串。
-
toUpperCase()
把一个字符串全部变为大写; -
toLowerCase()
把一个字符串全部变为小写; -
indexOf()
会搜索指定字符串出现的位置;
var s = 'hello, world';
s.indexOf('world'); // 返回7
s.indexOf('World'); // 没有找到指定的子串,返回-1
-
substring()
返回指定索引区间的子串。
var s = 'hello, world'
s.substring(0, 5); // 从索引0开始到5(不包括5),返回'hello'
s.substring(7); // 从索引7开始到结束,返回'world'
- 如果通过索引给数组赋值时,索引超过了范围,会引起数据大小的变化:
var arr = [1, 2, 3];
arr[5] = 'x';
arr; // arr变为[1, 2, 3, undefined, undefined, 'x']
- 常用的操作数组的方法
-
indexOf()
搜索一个指定的元素的位置;
var arr = [10, 20, '30', 'xyz'];
arr.indexOf(10); // 元素10的索引为0
-
slice()
截取Array
的部分元素,然后返回一个新的Array
:
var arr = ['A', 'B', 'C', 'D', 'E', 'F', 'G'];
arr.slice(0, 3); // 从索引0开始,到索引3结束,但不包括索引3: ['A', 'B', 'C']
不给slice()
传递任何参数来复制一个Array
。
-
push()
向Array
的末尾添加若干元素; -
pop()
则把Array
的最后一个元素删除掉; -
unshift()
向Array
的头部添加若干元素; -
shift()
则把Array
的第一个元素删除掉; -
sort()
会直接修改当前Array
的元素位置,对当前Array
进行排序; -
reverse()
把整个Array
的元素给掉个个,也就是反转; -
splice()
方法是修改Array
的“万能方法”,它可以从指定的索引开始删除若干元素,然后再从该位置添加若干元素:
var arr = ['Microsoft', 'Apple', 'Yahoo', 'AOL', 'Excite', 'Oracle'];
// 从索引2开始删除3个元素,然后再添加两个元素:
arr.splice(2, 3, 'Google', 'Facebook'); // 返回删除的元素 ['Yahoo', 'AOL', 'Excite']
arr; // ['Microsoft', 'Apple', 'Google', 'Facebook', 'Oracle']
// 只删除,不添加:
arr.splice(2, 2); // ['Google', 'Facebook']
arr; // ['Microsoft', 'Apple', 'Oracle']
// 只添加,不删除:
arr.splice(2, 0, 'Google', 'Facebook'); // 返回[],因为没有删除任何元素
arr; // ['Microsoft', 'Apple', 'Google', 'Facebook', 'Oracle']
-
concat()
方法把当前的Array
和另一个Array
连接起来,并返回一个新的Array
; -
join()
方法把当前Array
的每个元素都用指定的字符串连接起来,然后返回连接后的字符串:
var arr = ['A', 'B', 'C', 1, 2, 3];
arr.join('-'); // 'A-B-C-1-2-3'
如果Array
的元素不是字符串,将自动转换为字符串后再连接。
- JavaScript中能用
.
的地方就能用[ ]
代替:
xiaohong['name']; // '小红'
xiaohong.name; // '小红'
- JavaScript规定,访问不存在的属性不报错,而是返回
undefined
:
var xiaoming = {
name: '小明'
};
xiaoming.age; // undefined
- 检测
xiaoming
是否拥有某一属性:
- 可以用in操作符(无法确定属性来自本身还是来自继承)
'name' in xiaoming; // true
'grade' in xiaoming; // false
- 可以用hasOwnProperty()方法(判断一个属性是否自身拥有)
var xiaoming = {
name: '小明'
};
xiaoming.hasOwnProperty('name'); // true
xiaoming.hasOwnProperty('toString'); // false
-
if...else...
语句的执行特点是二选一,在多个if...else...
语句中,如果某个条件成立,则后续就不再继续判断了。 - JavaScript把
null
、undefined
、0
、NaN
和空字符串''
视为false
,其他值一概视为true
。 -
for
循环的一个变体是for ... in
循环,它可以把一个对象的所有属性依次循环出来:
var o = {
name: 'Jack',
age: 20,
city: 'Beijing'
};
for (var key in o) {
alert(key); // 'name', 'age', 'city'
}
- 由于
Array
也是对象,而它的每个元素的索引被视为对象的属性,因此,for ... in
循环可以直接循环出Array
的索引:
var a = ['A', 'B', 'C'];
for (var i in a) {
alert(i); // '0', '1', '2'
alert(a[i]); // 'A', 'B', 'C'
}
for ... in
对Array
的循环得到的是String
而不是Number
。
- Map是一组键(key)值(value)对的结构,具有极快的查找速度。
假设要根据同学的名字查找对应的成绩,如果用Array
实现,需要两个Array
:
var names = ['Michael', 'Bob', 'Tracy'];
var scores = [95, 75, 85];
给定一个名字,要查找对应的成绩,就先要在names
中找到对应的位置,再从scores
取出对应的成绩,Array
越长,耗时越长。
如果用Map
实现,只需要一个“名字”-“成绩”的对照表,直接根据名字查找成绩,无论这个表有多大,查找速度都不会变慢。用JavaScript写一个Map
如下:
var m = new Map([['Michael', 95], ['Bob', 75], ['Tracy', 85]]);
m.get('Michael'); // 95
-
Set
和Map
类似,也是一组key
的集合,但不存储value
。 -
Map
和Set
是ES6标准新增的数据类型。 - 遍历
Array
可以采用下标循环,遍历Map
和Set
就无法使用下标。ES6标准引入了新的iterable
类型,Array
、Map
和Set
都属于iterable
类型。 -
iterable
类型的集合可以通过新的for ... of
循环来遍历。for ... of循环和for ... in循环有何区别? - 函数体内部的语句执行到
return
时,函数就执行完毕,并将结果返回。如果没有return
,返回结果是undefined
。 -
arguments
只在函数内部起作用,指向当前函数的调用者传入的所有参数。利用其可以获得调用者传入的所有参数,最常用于判断传入参数的个数。 - 小心return语句(JavaScript引擎有一个在行末自动添加分号的机制,这可能让你栽到return语句的一个大坑):
function foo() {
return { name: 'foo' };
}
foo(); // { name: 'foo' }
function foo() {
return
{ name: 'foo' };
}
foo(); // undefined
- 定义一个计算圆面积的函数
square()
,它有两个参数:- r: 表示圆的半径;
- pi: 表示π的值,如果不传,则默认3.14。
一般写法:
function square(r, pi) {
var r,pi;
if(pi) {
return pi*r*r;
}
else {
return 3.14*r*r;
}
经典写法:
function square(r, pi) {
var r,pi;
r = (r || 0) > 0 ? r : 0;
pi = pi || 3.14;
return pi*r*r;
}
- 用
var
申明的变量是有作用域的:
- 变量在函数体内部申明,则该变量的作用域为整个函数体,在函数体外不可引用该变量;
- 不同函数内部的同名变量互相独立,互不影响;
- 嵌套函数时,内部函数可以访问外部函数定义的变量,反过来则不行;
- 内部函数定义了与外部函数重名的变量,则内部函数的变量将“屏蔽”外部函数的变量。
(这说明JavaScript的函数在查找变量时从自身函数定义开始,从“内”向“外”查找。)
- 函数会先扫描整个函数体的语句,把所有申明的变量“提升”到函数顶部,但不会提升变量的赋值:
function foo() {
var x = 'Hello, ' + y;
alert(x);
var y = 'Bob';
}
foo(); //Hello, undefined
- 不报错:变量
y
在稍后申明了。 - 显示
Hello, undefined
:说明变量y的值为undefined。JavaScript引擎自动提升了变量y
的声明,但不会提升变量y
的赋值。
- 全局作用域的变量实际上被绑定到
window
的一个属性。
直接访问全局变量a
和访问window.a
是完全一样的。 - 用
let
替代var
可以申明一个块级作用域的变量。 - ES6标准引入了新的关键字
const
来定义常量,const
与let
都具有块级作用域。 - 通常用全部大写的变量来表示“这是一个常量,不要修改它的值”:
const PI = 3.14;
。 - 绑定到对象上的函数称为方法,和普通函数没啥区别。
- 在一个方法内部,
this
是一个特殊变量,它始终指向当前对象。
这里有坑:详情移步 廖雪峰-方法
function getAge() {
var y = new Date().getFullYear();
return y - this.birth;
}
var xiaoming = {
name: '小明',
birth: 1990,
age: getAge
};
xiaoming.age(); // 25, 正常结果
getAge(); // NaN
- 以对象的方法形式调用,比如
xiaoming.age()
,该函数的this指向被调用的对象,也就是xiaoming
。 - 如果单独调用函数,比如
getAge()
,此时,该函数的this
指向全局对象,也就是window
。 - 即使把
xiaoming.age()
先给函数,再调用也不行。
var fn = xiaoming.age; // 先拿到xiaoming的age函数
fn(); // NaN
- ECMA决定,在
strict
模式下让函数的this
指向undefined
。 - 在方法函数内部定义的函数内
this
指向undefined
。
- 要指定函数的
this
指向哪个对象,可以用函数本身的apply
方法,它接收两个参数,第一个参数就是需要绑定的this
变量,第二个参数是Array
,表示函数本身的参数。 - 另一个与
apply()
类似的方法是call()
,唯一区别是:
-
apply()
把参数打包成Array
再传入; -
call()
把参数按顺序传入
-
js调用函数时加括号与不加括号的区别:
函数名其实就是指向函数体的指针 ,
- 不加括号, 可以认为是查看该函数的完整信息;
- 加括号 表示立即调用(执行)这个函数里面的代码(花括号部分的代码);
- 不加括号传参,相当于传入函数整体 ;
- 加括号传参,相当于将函数的返回值作为参数。
-
Array
的map()
方法
function pow(x) {
return x * x;
}
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
arr.map(pow); // [1, 4, 9, 16, 25, 36, 49, 64, 81]
-
reduce()
把结果继续和序列的下一个元素做累积计算:
var arr = [1, 3, 5, 7, 9];
arr.reduce(function (x, y) {
return x + y;
}); // 25
-
filter()
和map()
类似也接收一个函数,filter()
把传入的函数依次作用于每个元素,然后根据返回值是true
还是false
决定保留还是丢弃该元素。
var arr = [1, 2, 4, 5, 6, 9, 10, 15];
var r = arr.filter(function (x) {
return x % 2 !== 0;
});
r; // [1, 5, 9, 15]
- sort()方法也是一个高阶函数,它还可以接收一个比较函数来实现自定义的排序。
var arr = [10, 20, 1, 2];
arr.sort(function (x, y) {
if (x < y) {
return -1;
}
if (x > y) {
return 1;
}
return 0;
}); // [1, 2, 10, 20]
- 闭包,我不敢说太多,因为自己也没有明白得太清楚,但是我感觉这几篇文章值得看。javascript深入理解js闭包
深入理解javascript原型和闭包(完结) - 箭头函数相当于匿名函数,并且简化了函数定义,两种格式:
- 简化格式
- 单参数
x => x * x
上面的箭头函数相当于:
function (x) {
return x * x;
}
* 多参数
(x, y) => x * x + y * y
* 无参数
() => 3.14
- 标准格式
x => {
if (x > 0) {
return x * x;
}
else {
return - x * x;
}
}
不要使用·new Number()·、·new Boolean()·、·new String()·创建包装对象;
用
parseInt()
或parseFloat()
来转换任意类型到number
;用
String()
来转换任意类型到string
,或者直接调用某个对象的toString()
方法;通常不必把任意类型转换为
boolean
再判断,因为可以直接写if (myVar) {...}
;typeof
操作符可以判断出number
、boolean
、string
、function
和undefined
;判断Array要使用
Array.isArray(arr)
;判断
null
请使用myVar === null
;判断某个全局变量是否存在用
typeof window.myVar === 'undefined'
;函数内部判断某个变量是否存在用
typeof myVar === 'undefined'
。Date对象用来表示日期和时间。要获取系统当前时间,用:
var now = new Date();
now; // Wed Jun 24 2015 19:49:22 GMT+0800 (CST)
now.getFullYear(); // 2015, 年份
now.getMonth(); // 5, 月份,注意月份范围是0~11,5表示六月
now.getDate(); // 24, 表示24号
now.getDay(); // 3, 表示星期三
now.getHours(); // 19, 24小时制
now.getMinutes(); // 49, 分钟
now.getSeconds(); // 22, 秒
now.getMilliseconds(); // 875, 毫秒数
now.getTime(); // 1435146562875, 以number形式表示的时间戳
- 正则表达式的设计思想是用一种描述性的语言来给字符串定义一个规则,凡是符合规则的字符串,我们就认为它“匹配”了,否则,该字符串就是不合法的。
- js正则表达式基本语法(精粹)
- 对象的两个基本概念:
- 类:类是对象的类型模板,例如,定义Student类来表示学生,类本身是一种类型,Student表示学生类型,但不表示任何具体的某个学生;
- 实例:实例是根据类创建的对象,例如,根据Student类可以创建出xiaoming、xiaohong、xiaojun等多个实例,每个实例表示一个具体的学生,他们全都属于Student类型。
JavaScript不区分类和实例的概念,而是通过原型(prototype)来实现面向对象编程。
-
window
对象不但充当全局作用域,而且表示浏览器窗口。有innerWidth
和innerHeight
属性,可以获取浏览器窗口的内部宽度和高度。内部宽高是指除去菜单栏、工具栏、边框等占位元素后,用于显示网页的净宽高。outerWidth
和outerHeight
属性,可以获取浏览器窗口的整个宽高。 - DOM是一个树形结构。操作一个DOM节点实际上就是这么几个操作:
- 更新:更新该DOM节点的内容;
-
innerHTML
可以修改一个DOM节点的文本内容,还可以通过HTML片段修改DOM节点内部的子树; -
innerText
或textContent
修改一个DOM节点的文本内容,无法设置任何HTML标签。innerText不返回隐藏元素的文本,而textContent返回所有文本。
-
- 遍历:遍历该DOM节点下的子节点;
- 添加:在该DOM节点下新增一个子节点;
- 如果这个DOM节点是空的,直接使用
innerHTML = '<span>child</span>'
,不是空的,那就不能这么做,因为innerHTML会直接替换掉原来的所有子节点; - 使用
appendChild
,把一个子节点添加到父节点的最后一个子节点;
*使用insertBefore
,把子节点插入到指定的位置。
- 如果这个DOM节点是空的,直接使用
- 删除:将该节点从HTML中删除。
- 要删除一个节点,首先要获得该节点本身以及它的父节点,然后,调用父节点的
removeChild
把自己删掉
- 要删除一个节点,首先要获得该节点本身以及它的父节点,然后,调用父节点的
感谢各位阅读,欢迎指正错误!!!