之前看廖雪峰的git教程觉得很好,当时就注意到有js的教程,在w3school看了觉得函数部分讲的不太好,所以来看廖雪峰的
下面笔记开始
基本语法
1. string
- 字符串拼接
如果有很多变量需要连接,用+号就比较麻烦。ES6新增了一种模板字符串,表示方法和上面的多行字符串一样,但是它会自动替换字符串中的变量:
var name = '小明';
var age = 20;
alert(`你好, ${name}, 你今年${age}岁了!`);
- 字符串是不可变的,当然同样的方法,array里的元素是可以被改变的
var s = 'Test';
s[0] = 'X';
alert(s); // s仍然为'Test'
- string的几个常用方法
var s = 'Hello';
s.toUpperCase(); // 返回'HELLO'
s.toLowerCase();//返回‘hello’
s.indexOf('ll');//返回2;如果返回-1说明没有匹配字符串
s.substring(0,3)//返回[0,3)字符串,区间左闭右开;如果只有一个参数,返回从该参数到最后的substring
2. array
- 如果通过索引赋值时,索引超过了范围,不会报错!
var arr = [1, 2, 3];arr[5] = 'x';arr; // arr变为[1, 2, 3, undefined, undefined, 'x']
- 也有indexOf();的方法,也是如果找不到对应元素就返回-1,其他情况返回对应索引
- slice() similar as string.substring()
arr.slice(0, 3); // 从索引0开始,到索引3结束,但不包括索引3
arr.slice(3); // 从索引3开始到结束: ['D', 'E', 'F', 'G']
arr.slice();//整个原arr,但是原arr的复制品,用===判定是否相同会返回false
- arr.push()------arr.pop(),尾部追加和推出最后一个元素
arr.unshift()------arr.shift(),头部添加和删除第一个元素 - 排序,反转
arr.sort();
arr.reverse();
- splice:从指定的索引开始删除若干元素,然后再从该位置添加若干元素
var arr = ['Microsoft', 'Apple', 'Yahoo', 'AOL', 'Excite', 'Oracle'];
// 从索引2开始删除3个元素,然后再添加两个元素:
arr.splice(2, 3, 'Google', 'Facebook');
// 只删除,不添加:
arr.splice(2, 2);
// 只添加,不删除:
arr.splice(2, 0, 'Google', 'Facebook');
- concat,把作为参数的数组拼接在原数组后面
arr.concat([1, 2, 3])
请注意,concat()方法并没有修改当前Array,而是返回了一个新的Array。
实际上,concat()方法可以接收任意个元素和Array,并且自动把Array拆开,然后全部添加到新的Array里:
var arr = ['A', 'B', 'C'];
arr.concat(1, 2, [3, 4]); // ['A', 'B', 'C', 1, 2, 3, 4]
- join 用指定符号连接数组中元素
var arr = ['A', 'B', 'C', 1, 2, 3];
arr.join('-'); // 'A-B-C-1-2-3'
3. object
- 可以用.propName或["propName"]这两种方式引用obj的prop
- 删除属性:delete xiaoming.school;
删除一个不存在的school属性也不会报错 - 用in判断obj有没有某属性(包括自己的也包括继承来的):
'name' in xiaoming;
用hasOwnProperty判断是不是自身的(不包括继承来的):xiaoming.hasOwnProperty('name');
4. if
跟其他语言一样的特性,注意逻辑清楚才能写对代码
5. loop
- for循环的3个条件都是可以省略的
for( ; ; ){...}
,如果没有退出循环的判断条件,就必须使用break语句退出循环,否则就是死循环 - for in 可以把obj的所以prop列举出来
var o = { name: 'Jack', age: 20, city: 'Beijing'};
for (var key in o) {
alert(key); // 'name', 'age', 'city'
}
array也是obj,所以arr也可以用
var a = ['A', 'B', 'C'];
for (var i in a) {
alert(i); // '0', '1', '2'注意这里是string不是num
alert(a[i]); // 'A', 'B', 'C'
}
- while 和do while,都完全可以用for取代,尤其do while用的更少
6. Map,Set
- JavaScript的默认对象表示方式{}可以视为其他语言中的Map或Dictionary的数据结构,即一组键值对。要求key必须为string,但ES6规范引入了新的数据类型Map,支持其他类型的key,比如数字
- map常用操作:(多次对一个key放入value,后面的值会把前面的值冲掉)
var m = new Map(); // 空Map
m.set('Adam', 67); // 添加新的key-value
m.has('Adam'); // 是否存在key 'Adam': true
m.get('Adam'); // 67
m.delete('Adam'); // 删除key 'Adam'
m.get('Adam'); // undefined
- set很像java中set,类似数组但不存储重复值
方法也类似,比如:
s.add();
s.delete();
- iterable类型可以用for of 来遍历
Map Set Array都属于iterable类型,
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
alert(x);
}
for (var x of s) {
// 遍历Set
alert(x);
}
for (var x of m) {
// 遍历Map
alert(x[0] + '=' + x[1]);
}
- iterable内置的forEach方法
var a = ['A', 'B', 'C'];
a.forEach(function (element, index, array) {
// element: 指向当前元素的值 // index: 指向当前索引 // array: 指向Array对象本身
alert(element);
});
//set没有索引,所以第二个参数是sameElement
var s = new Set(['A', 'B', 'C']);
s.forEach(function (element, sameElement, set) {
alert(element);
});
//map:k-v
var m = new Map([[1, 'x'], [2, 'y'], [3, 'z']]);
m.forEach(function (value, key, map) {
alert(value);
});
//如果对其中一些参数没有兴趣,可以直接省略不写
函数 function
-
arguments,它只在函数内部起作用,并且永远指向当前函数的调用者传入的所有参数。arguments类似Array但它不是一个Array。
argument可以获得传入的所有参数,即使函数不定义任何参数
argument常用语判断传入参数的个数例子
rest 获取剩余的参数,有点像go里的可变参数,rest只能写在最后,如果正常参数都没传够,rest是一个空数组,而不是undefined
function foo(a, b, ...rest) {
console.log('a = ' + a);
console.log('b = ' + b);
console.log(rest);}
foo(1, 2, 3, 4, 5);
// 结果:// a = 1// b = 2// Array [ 3, 4, 5 ]
- return 如果要返回一堆记得加大括号,直接return 换行 returnContent,returnContent这句将执行不到(因为return后会自动加;程序就返回了)
- 变量作用域:
a...函数内的作用域是函数内,所以不同函数可以有同名变量;
b...js引擎会自动提升所有变量的声明但不提升赋值;
c...变量从内向外查找,内部会屏蔽外部重名变量
d...JavaScript默认有一个全局对象window,全局作用域的变量实际上被绑定到window的一个属性,有个好的方法就是把自己所有变量和函数都绑定到一个全局变量中
// 唯一的全局变量
MYAPP:var MYAPP = {};
// 其他变量:
MYAPP.name = 'myapp';
MYAPP.version = 1.0;
// 其他函数:
MYAPP.foo = function () {
return 'foo';
};
e...let在for循环中可以替代var,声明一个块级作用域的变量for (let i=0; i<100; i++) {...}
f...常量 const,默认不能被更改的,如果又重新赋值想修改,不会报错,但修改不会生效
- 在一个对象中绑定函数,称为这个对象的方法,通过object.method()的形式调用(确保this指向正确)
- 有个this指向的坑,可能是你以为指向了obj,然而指向了全局window,需要var that=this;例子;或者用apply,例子还在左边的链接里,用途是会把想指向的obj打包成array当做参数传入,call也有类似的作用,把参数按顺序传入
Math.max.apply(null, [3, 5, 4]); // 5 Math.max.call(null, 3, 5, 4); // 5
- 装饰器,利用apply,动态改变函数行为。例子是想统计调用了多少次某方法,链接上面👆
- higher-order function 一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。跟go里也有地方很像,比如filepath.Walk(root,walkfunc)这样老根其他语言比较好像不太好。。憋学我
- 讲map的时候的一个例子,看起来好不习惯,就是把原arr中都map(同过一个映射关系映射成别的样子)成string(我也不知道该怎么说了
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]
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
arr.map(String); // ['1', '2', '3', '4', '5', '6', '7', '8', '9']
如果不给pow传参数,说明pow是某obj的方法,比如arr.map所以直接用obj调用了,可能是这样吧.或者理解成map前面那货是后面这个函数的参数,看怎么理解记住的快了。
map()作为高阶函数,事实上它把运算规则抽象了,因此,我们不但可以计算简单的f(x)=x2,还可以计算任意复杂的函数,比如,把Array的所有数字转为字符串。
- reduce,根据例子加之前对mapreduce的印象大概就是把同类,或者子问题,先解决,从而帮助解决其他同类或者父问题
[x1, x2, x3, x4].reduce(f) = f(f(f(x1, x2), x3), x4)
应用,比如求和:
var arr = [1, 3, 5, 7, 9];
arr.reduce(function (x, y) { return x + y;}); // 25
- filter,把array的某些元素过滤掉返回剩下元素,Array的filter()也接收一个函数。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是true还是false决定保留还是丢弃该元素。例子
- sort 大小写敏感,所有元素转string后排序,会直接修改array
- 闭包(多年后我终于又回来看了闭包
function count() {
var arr = [];
for (var i=1; i<=3; i++) {
arr.push(function () {
return i * i;
});
}
return arr;
}
var results = count();
var f1 = results[0];
var f2 = results[1];
var f3 = results[2];
f1(); // 16
f2(); // 16
f3(); // 16
这段代码是个典型的闭包例子,function () {return i * i; }
这个funtion引用了函数外的变量i,
最后的输出结果都为16,可以看出,在执行f1,f2,f3时,i=4, 为什么不是想我们想的那样输出 1, 4, 9
- 先看results这里,results=count(); count方法调用了,也就是说results现在是count()函数的返回值,即:results=arr;
- 那arr里存的是什么,arr[0],arr[1],arr[3]里现在存的都是
function () {return i * i; }
,这个function通过push方法存入数组; - var f1 = results[0]其实就是arr[0]赋值给了f1,也就是说f1现在是这个函数
function () {return i * i; }
; - f1();即执行该函数。 但我们刚刚说过
count()
已经执行完了,所以这时候i=4,是i进不去for循环的值,所以f1();执行结果=16,剩下两个同理。
我们并没有得到想要的答案,下面看解决方案
arr.push((function (n) {
return function () {
return n * n;
}
})(i));
我们通过n来保留当时的i值,
当我们执行count()的时候,向arr中push的元素变为
(function (n) {
return function () {
return n * n;
}
})(i)
这个东西,函数后面跟有小括号即执行函数,小括号中i即function需要接收的参数n,所以我们push到数组里的三个元素其实是这个东西的执行结果(上面这个函数的返回值):function () {return 1 * 1; }
,function () {return 2* 2; }
,function () {return 3 * 3; }
问题解决。
剩下两个闭包例子不看了吧,随缘吧,用一用应该就会了
函数还剩下两个部分,箭头函数和generator, 都是ES6新增内容,先跳过了,先看下对象和DOM操作方面的
Object
- typeOf看类型
typeof 123; // 'number'
typeof NaN; // 'number'
typeof 'str'; // 'string'
typeof true; // 'boolean'
typeof undefined; // 'undefined'
typeof Math.abs; // 'function'
typeof null; // 'object'
typeof []; // 'object'
typeof {}; // 'object'