参考自: https://www.liaoxuefeng.com/wiki/1022910821149312/1023024358748480
个人简单总结, 加入自己理解
基础语法
字符串
常用方法
toUpperCase()
toLowerCase()
indexOf()
-
substring()
返回指定区间字符串,
一个参数表示结束位置, 两个参数第一个表示开始第二个表示结束
var s = 'hello, world' s.substring(0, 5); // 从索引0开始到5(不包括5),返回'hello' s.substring(7); // 从索引7开始到结束,返回'world'
数组
常用的方法
indexOf() 查找元素指定位置
-
slice() 它截取
Array
的部分元素,然后返回一个新的Array
var arr = ['A', 'B', 'C', 'D', 'E', 'F', 'G']; arr.slice(0, 3); // 从索引0开始,到索引3结束,但不包括索引3: ['A', 'B', 'C'] arr.slice(3); // 从索引3开始到结束: ['D', 'E', 'F', 'G']
-
sort排序 递增, 可以自定义
arrInt = arrInt.sort(function (a, b) { return b - a; // 后面比前面大的时候就要交换, 也就是从 大 ==> 小 })
reverse反转数组
-
splice()
删除添加函数,最厉害的
, 三个参数(开始位置, 删除个数, 添加的值)其中添加的值可以有很多个
-
concat 数组拼接
注意,
concat()
方法并没有修改当前Array
,而是返回了一个新的Array
。返回的数组是一维的
-
join 把当前
Array
的每个元素都用指定的字符串连接起来,然后返回连接后的字符串返回的是连接后的字符串
pop和push
-
unshift和shift 是队列用的
- unshift 队头添加
- shift 队尾巴添加
对象
的常用方法
-
循环查看对象, 用for…in...
var o = { name: 'Jack', age: 20, city: 'Beijing' }; for (var key in o) { console.log(key); // 'name', 'age', 'city' }
要过滤掉对象继承的属性,用
hasOwnProperty()
来实现:if (o.hasOwnProperty(key)) {}
注意,
for ... in
对Array
的循环得到的是String
而不是Number
。var a = ['A', 'B', 'C']; for (var i in a) { console.log(i); // '0', '1', '2' console.log(a[i]); // 'A', 'B', 'C' }
也即是下标是字符串
-
map
: JavaScript的对象中, 键必须是字符串。但实际上Number或者其他数据类型作为键也是非常合理的。为了解决这个问题,最新的ES6规范引入了新的数据类型Map,键可以是任意类型
var m = new Map(); // 空Map m.set('Adam', 67); // 添加新的key-value m.set('Bob', 59); m.has('Adam'); // 是否存在key 'Adam': true m.get('Adam'); // 67 m.delete('Adam'); // 删除key 'Adam' m.get('Adam'); // undefined
-
set
: Set和Map类似,也是一组key的集合,但不存储value
。由于key不能重复
,所以,在Set中,没有重复的key。需要提供一个Array
作为输入,或者直接创建一个空Set
Set
与Array
类似,但Set
没有索引var s1 = new Set(); // 空Set var s2 = new Set([1, 2, 3]); // 含1, 2, 3 s.add(4);// Set {1, 2, 3, 4} s.add(4); // 仍然是 Set {1, 2, 3, 4} var s = new Set([1, 2, 3]);// Set {1, 2, 3} s.delete(3); // Set {1, 2}
iterable
循环遍历数组, map和set
使用for ... of
循环来遍历。for ... of
循环是ES6引入的新的语法
var a = ['A', 'B', 'C'];
var s = new Set(['A', 'B', 'C']);
var m = new Map([[1, 'x'], [2, 'y'], [3, 'z']]);
for (var x of a) { // 遍历Array
console.log(x);
}
for (var x of s) { // 遍历Set
console.log(x);
}
for (var x of m) { // 遍历Map
console.log(x[0] + '=' + x[1]); // 第一个是键, 第二个是值
}
for ... in
循环遍历的实际上是对象的属性名称var a = ['A', 'B', 'C']; a.name = 'Hello'; for (var x in a) { console.log(x); // '0', '1', '2', 'name' }
for ... of
循环则完全修复了这些问题,它只循环集合本身的元素:var a = ['A', 'B', 'C']; a.name = 'Hello'; for (var x of a) { console.log(x); // 'A', 'B', 'C' }
forEach
foEach没有返回值
Set
与Array
类似,但Set
没有索引,因此回调函数的前两个参数都是元素本身:
var s = new Set(['A', 'B', 'C']);
s.forEach(function (element, sameElement, set) {
console.log(element);
});
Map
的回调函数参数依次为value
、key
和map
本身:
var m = new Map([[1, 'x'], [2, 'y'], [3, 'z']]);
m.forEach(function (value, key, map) {
console.log(value);
});
JavaScript的函数调用不要求参数必须一致,因此可以忽略它们。例如,只需要获得Array
的element
var a = ['A', 'B', 'C'];
a.forEach(function (element) {
console.log(element);
});
函数
函数参数arguments
代表实际传入的参数, 可以当做一个数组来取用, 但是它不是数组
function abs() {
if (arguments.length === 0) {
return 0;
}
var x = arguments[0];
return x >= 0 ? x : -x;
}
函数参数rest
代表, 比如我写了两个参数, 但是传入进来了5个, 那么多出来的三个就是rest, 写法如下:
function foo(a, b, ...rest) {
console.log(rest);
}
JavaScript的作用域是函数作用域, 也即是函数内部的变量不影响外部, 隔离开了
但是内部可以访问上级的变量. 冒泡向上找
变量提升
定义在函数内部的变量, 会被先提前声明到最顶部, 执行函数前会先扫描一遍函数内部
如果不写变量声明就会被绑定到全局作用域, 绑定到window下
foo = 'A`; // 没有写var或let就被当做是全局作用域下的属性
解构赋值
var x=1, y=2;
[x, y] = [y, x]
es6内部可以用解构赋值
, 来进行多变量赋值
var person = {
name: '小明',
age: 20,
address: {
city: 'Beijing',
street: 'No.1 Road',
zip: '100001'
}
};
var {name, address: {city, zip}} = person;
let [, , z] = ['hello', 'JavaScript', 'ES6']; // 忽略前两个元素,只对z赋值第三个元素
有些时候,如果变量已经被声明了,再次赋值的时候,正确的写法也会报语法错误:
// 声明变量:
var x, y;
// 解构赋值:
{x, y} = { name: '小明', x: 100, y: 200};
// 语法错误: Uncaught SyntaxError: Unexpected token =
这是因为JavaScript引擎把{
开头的语句当作了块处理,于是=
不再合法。解决方法是用小括号括起来:
({x, y} = { name: '小明', x: 100, y: 200});
apply和call
Math.max.apply(null, [3, 5, 4]); // 5
Math.max.call(null, 3, 5, 4); // 5
-
apply
第二个参数是数组把参数打包成
Array
再传入对普通函数调用我们通常把
this
绑定为null
。JavaScript的所有对象都是动态的,即使内置的函数,我们也可以重新指向新的函数。
现在假定我们想统计一下代码一共调用了多少次
parseInt()
,可以把所有的调用都找出来,然后手动加上count += 1
,不过这样做太傻了。最佳方案是用我们自己的函数替换掉默认的parseInt()
var count = 0; var oldParseInt = parseInt; // 保存原函数 window.parseInt = function () { count += 1; return oldParseInt.apply(null, arguments); // 调用原函数 };
// 测试: parseInt('10'); parseInt('20'); parseInt('30'); console.log('count = ' + count); // 3
call()
把参数按顺序传入
高阶函数
map/reduce
-
map映射
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]; arr.map(String); // ['1', '2', '3', '4', '5', '6', '7', '8', '9'] arr.map((item)=>{ return parseInt(item) });
-
reduce
reduce()
把结果继续和序列的下一个元素做累积计算var arr = [1, 3, 5, 7, 9]; arr.reduce(function (x, y) { return x + y; }); // 25
filter
filter是用于过滤
var arr = ['A', '', 'B', null, undefined, 'C', ' '];
var r = arr.filter(function (s) {
return s && s.trim(); // 注意:IE9以下的版本没有trim()方法,
// trim()删除头尾空白字符, 这里删了就变空了就返回false
});
r; // ['A', 'B', 'C']
filter()
接收的回调函数,其实可以有多个参数。通常我们仅使用第一个参数,表示Array
的某个元素。
也可以接受另外两个参数, 表示元素的位置和数组本身:
var arr = ['A', 'B', 'C'];
var r = arr.filter(function (element, index, self) {
console.log(element); // 依次打印'A', 'B', 'C'
console.log(index); // 依次打印0, 1, 2
console.log(self); // self就是变量arr
return true;
});
sort
var arr = [10, 20, 1, 2];
arr.sort(function (x, y) {
if (x < y) {
return -1; // 表示x排在y左边
}
if (x > y) {
return 1; // 表示x排在y右边
}
return 0; // 表示顺序不变
});
console.log(arr); // [1, 2, 10, 20]
Array本身有很多高阶函数
-
every() 判断所有元素是否符合测试条件, 接受一个参数, 是就返回true
var arr = ['Apple', 'pear', 'orange']; console.log(arr.every(function (s) { return s.toLowerCase() === s; })); // false, 因为不是每个元素都全部是小写
-
find() 查找符合条件的第一个元素,如果找到了,返回这个元素,否则,返回
undefined
var arr = ['Apple', 'pear', 'orange']; console.log(arr.find(function (s) { return s.toLowerCase() === s; })); // 'pear', 因为pear全部是小写
findIndex() 和find类似, 不过返回的是索引罢了
forEach()和map()类似, 但是不会返回数组, 常用于遍历数组
参数为
(值, 下标, 数组)
闭包
因为JavaScript是函数内作用域
利用这一点可以在函数内建立函数来进行
并且他的作用域是冒泡形式向上的
参考: https://segmentfault.com/a/1190000002778015
闭包的定义
- 可以访问外部函数作用域中变量的
函数
-
被内部函数访问的外部函数的变量可以保存在外部函数作用域内而不被回收---这是核心
,后面我们遇到闭包都要想到,我们要重点关注被闭包引用的这个变量。
闭包例子:
var person= function(){
//变量作用域为函数内部,外部无法访问
var name = "default";
return {
getName : function(){
return name;
},
setName : function(newName){
name = newName;
}
}
}();
console.log(person.name);//直接访问,结果为undefined
console.log(person.getName()); //default
person.setName("jozo");
console.log(person.getName()); //jozo
闭包作用
- 可以读取外部函数的变量, 并且让这些
变量保持在内存中
例如给每个li添加点击事件
var oli = document.getElementsByTagName('li');
var i;
for(i = 0;i < 5;i++){
oli[i].onclick = function(){
alert(i);
}
}
console.log(i); // 5
//执行匿名函数
(function(){
alert(i); //5
}());
上面是一个经典的例子,是没有用到闭包
, 我们都知道点击执行结果是都弹出5, 可以用闭包解决这个问题
a. 先来分析没用闭包前
的情况:for循环中,我们给每个li点击事件绑定了一个匿名函数
,匿名函数中返回了变量i的值,当循环结束后,变量i的值变为5,此时我们再去点击每个li,也就是执行相应的匿名函数
(看上面的代码),这是变量i已经是5了,所以每个点击弹出5. 因为这里返回的每个匿名函数都是引用了同一个变量i,如果我们新建一个变量保存循环执行时当前的i的值,然后再让匿名函数应用这个变量,最后再返回这个匿名函数,这样就可以达到我们的目的了,这就是运用闭包来实现的!
b. 再来分析下运用闭包时的情况:
var oli = document.getElementsByTagName('li');
var i;
for(i = 0;i < 5;i++){
oli[i].onclick = (function(num){
var a = num; // 为了说明问题
return function(){
alert(a);
}
})(i)
}
console.log(i); // 5
这里for循环执行时,给点击事件绑定的匿名函数传递i后立即执行返回一个内部的匿名函数,因为参数是按值传递的,所以此时形参num保存的就是当前i的值
,然后赋值给局部变量 a,然后这个内部的匿名函数一直保存着a的引用
,也就是一直保存着当前i的值。 所以循环执行完毕后点击每个li,返回的匿名函数执行弹出各自保存的 a 的引用的值
。
可以避免命名重复全局污染问题, 也就是说命名可以和全局名字一样, 但是他们互不影响的
-
要访问函数内部变量就可以用闭包来实现
function f1(){ var n=999; function f2(){ alert(n); // 999 } return f2; }
缺点
闭包会常驻内存, 会增大内存使用, 使用不当就会有内存泄漏
generator
生成器
就是使用yield可以类似中断一样, 通过调用下一步来运行
function* fib(max) {
var
t,
a = 0,
b = 1,
n = 0;
while (n < max) {
yield a;
[a, b] = [b, a + b];
n ++;
}
return;
}
var f = fib(5);
f.next(); // {value: 0, done: false}
f.next(); // {value: 1, done: false}
f.next(); // {value: 1, done: false}
f.next(); // {value: 2, done: false}
f.next(); // {value: 3, done: false}
f.next(); // {value: undefined, done: true}
标准对象
data
对象
JavaScript的Date对象月份值从0开始,牢记0=1月,1=2月,2=3月,……,11=12月
获取系统当前时间
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形式表示的时间戳
RegExp
正则匹配, 可以用new RegExp()创建正则
var re = /ABC\-001/;
var re = new RegExp('ABC\\-001');
re.test(字符);
切分字符串
'a,b;; c d'.split(/[\s\,\;]+/); // ['a', 'b', 'c', 'd']
分组, 可以用exec()
方法提取出子串
var re = /^(\d{3})-(\d{3,8})$/;
re.exec('010-12345'); // ['010-12345', '010', '12345']
JSON
json字符串转为json对象
var obj = eval('('+str+')');
var obj = str.parseJSON();
var obj = JSON.parse(str);
json对象转为json字符串
var str = obj.toJSONString();
var str = JSON.stringify(obj);
面向对象编程
关于JavaScript继承可以看我的文章:
https://www.jianshu.com/p/70e86e2d2636
浏览器对象
window
window
对象有innerWidth
和innerHeight
属性,可以获取浏览器窗口的内部宽度和高度。内部宽高是指除去菜单栏、工具栏、边框等占位元素后,用于显示网页的净宽高。
navigator
- navigator.appName:浏览器名称;
- navigator.appVersion:浏览器版本;
- navigator.language:浏览器设置的语言;
- navigator.platform:操作系统类型;
- navigator.userAgent:浏览器设定的
User-Agent
字符串。
screen
- screen.width:屏幕宽度,以像素为单位;
- screen.height:屏幕高度,以像素为单位;
- screen.colorDepth:返回颜色位数,如8、16、24。
location
location.protocol; // 'http'
location.host; // 'www.example.com'
location.port; // '8080'
location.pathname; // '/path/index.html'
location.search; // '?a=1&b=2'
location.hash; // 'TOP'
DOM操作
常见的dom操作
添加节点
document.creatElement()
div.appendChild()
获取父元素
div.appendChild()
获取子元素
div.childNodes()
删除节点
div.removeChild()
promise
Promise().then().then....catch() 多任务串行执行.
Promise.all([p1,p2,...]) 多任务并行执行
都要成功才进入then,返回结果数组.
Promise.race([p1,p2,...]) 多任务赛跑.
then()和catch(),谁先调用算谁的,其它任务中断.