入门
数据类型和变量
数据类型
Number
- +、-、*、/四则运算,%求余数
String
- 单引号或双引号包裹
- 转义字符:\
- 多行字符串:ES6新方式:使用
...
- 连接字符串:用+号
模板字符串
var name = "lxf";
var age = 26;
var msg = '您好,$(name),您今年$(age)岁。';
操作字符串
- 获取字符串的长度:
变量名.length
- 通过索引访问对应字符串
- 字符串是不可变的,对某个索引赋值,不会报错也不会有效果
- 字符串全部变大写:
变量名.toUpperCase( )
- 字符串全部变小写:
变量名.toLowerCase( )
- 检索字符的位置:
变量名.indexOf(字符)
,返回字符的索引值,如果没有这个字符,则返回-1 - 返回指定区间的字符:
变量名.substring(开始位置,结束位置 )
,返回的字符串不包括结束位置的字符
Boolean
- true、false
逻辑运算符
- &&逻辑与运算符:所有都为true时,结果才是true
- ||逻辑或运算符:其中一个为true,结果就是true
- !逻辑非运算符:单目运算符,true变false,false变true
比较运算符
- 包括:>,<,>=,<=,==,===
- ==:先自动转换数据类型,再比较
- ===:比较原始的数据类型
- NaN:与所有其他值都不相等,包括它自己。
- 判断NaN的方法:isNaN( )函数
- 不要直接比较两个浮点小数
null
- 表示一个空的值
undefined
- 未定义
Array
- 数组:一组按顺序排列的集合,集合的每个值称为元素,数组可以包括任意数据类型
- 创建数组方式:
new Array()
、var arr = []
- 访问数组中的元素可以使用索引值,索引值从0开始
- 访问超出数组长度的索引值时,返回undefined
- 获取数组长度:
数组.length
- 给length赋值会改变数组的长度,多出来的值为undefined
- 通过访问索引值并对索引赋值,会修改数组,如果索引值超出长度范围,该索引的值为赋的值,中间多出来的索引的值为undefined
- 搜索指定元素的索引值:
数组.indexOf(元素)
,未找到会返回-1 - 截取指定范围的元素并返回一个新数组:
数组.slice(开始位置,结束位置)
,新的数组不包含结束位置的元素,不给slice传递任何参数,则会从头到尾复制数组
push和pop
- push:向数组末尾添加若干元素
数组.push(x,y,z,...)
- pop:删除数组最后一个元素
数组.pop( )
,对空数组使用pop会返回undefined
unshift和shift
- unshift:向数组的头部添加若干元素
arr.unshift(x,y,z,...)
- shift:删除数组的第一个元素
arr.shift()
,空数组使用会返回undefined
sort
- 对当前数组进行排序,会修改元素的位置,直接调用
sort()
会按照默认条件排序
reverse
- 将数组反转排列
var arr = [1,2,3];
arr.reverse();
arr; // [3,2,1]
splice
- 从指定的位置删除若干元素,并从该位置添加新元素
- splice(开始位置,删除元素数量,新的元素,新的元素,...)
- 只删除不添加:splice(开始位置,删除元素数量)
- 只添加不删除:splice(开始位置,0,新的元素,新的元素,...)
var arr = [1,2,3];
arr.splice(1,2,'a','b','c'); // [2,3]
arr; // [1,'a','b','c']
concat
- concat()将数组与其他数组组合起来,并返回一个新的数组
- concat并没有修改原数组,而是返回一个新数组
var arr = [1,2,3];
var newArr = arr.concat([9,8,7]);
newArr; // [1,2,3,9,8,7]
arr; // [1,2,3]
- concat可以接收任意个元素和数组,并会把接收到的数组拆开再添加到返回的新的数组中
var arr = [1,2,3];
var newArr = arr.concat('a','b',[9,8,7]);
newArr; // [1,2,3,'a','b',9,8,7]
arr; // [1,2,3]
join
- 将数组内的所有元素按照指定的字符连接起来,然后返回连接后的字符串
var arr = [1,2,3];
arr.join('+'); // '1+2+3'
Object
- 对象:一组由键值对组成的无序集合
- 对象的键名是字符串类型,值可以是任意数据类型
- 键名又称对象的属性
- 获取对象的属性:
obj.属性名
/obj["属性名"]
- 访问不存在的属性时,返回undefined
- 新增属性:
obj.属性 = 值
- 删除属性:
delete obj.属性
- 删除不存在的属性时并不会报错
- 判断属性是否是obj自有的而不是继承的:
obj.hasOwnProperty()
变量
- 变量可以是任意数据类型
- 变量名可以包含:大小写英文、数字、$、_
- 变量名不可以是数字开头
- 变量名不能是关键字
strict模式
- 开启:'use strict'
条件判断
- if...else
- null、undefined、0、NaN、空字符串视为false
循环
for循环
- 省略三个条件将无限循环,需要使用break退出循环
for...in
- 将对象的所有属性依次循环出来
var obj = {
name: 'lxf',
age: 26
};
for(var prop in obj){
if(obj.hasOwnProperty(prop)){ // 过滤掉继承的属性
console.log(prop);
}
}
- 由于Array也是对象,而它每个元素的索引被视为对象的属性,因此for...in可以循环出数组的索引
- for...in循环出的结果是字符串
var arr = ['a','b','c'];
for(var item in arr){
alert(item); // "0,1,2"
alert(arr[item]); // 'a','b','c'
}
while循环
- 只有一个判断条件,条件满足就不断循环
do...while
- 先执行循环,再判断
Map和Set
- ES6
Map
- map是一组键值对结构,拥有极快的查找速度
- 初始化:需要一个二维数组,或直接初始化一个空的map
var m = new Map();
m.set("name","lxf"); // 添加键值对
m.has("name"); // 是否含有name属性
m.get("name"); // 返回值lxf
m.delete("name"); // 删除name
m.get('name'); // 不存在的返回undefined
Set
- set是一组key的集合,没有重复的key
- 创建:
var s1 = new Set()
或var s2 = new Set([])
- 重复元素的set中会被自动过滤
- 添加:
s1.add(key)
- 删除:
s1.delete(key)
iterable
- ES6引入的新的类型
- Array、Map、Set都属于iterable类型
for...of
- 只循环集合本身的元素
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]);
}
forEach()
- ES5.1的方法
- iterable内置的方法,接收一个函数,每次迭代就自动回调该函数
- 不需要的参数可以省略
var arr = [1,2,3];
arr.forEach(function(element, index, array){
// element:当前元素的值
// index:当前元素的索引值
// array:数组本身
alert(element);
});
var s = new Set(['1','a','c']);
s.forEach(function(element, sameElement, set){
// element, sameElement:当前元素本身
// set:该set
alert(element);
})
var m = new Map([[1,11],['asd',222],[3,'zxc']]);
m.forEach(function(value, key, map){
// value:当前属性的值
// key:当前属性
// map:map本身
alert(key + '=' + value);
})
函数
- 函数体内语句执行到return时,函数就执行完毕,并返回结果;没有return时,函数体内代码执行完毕后也会返回结果,但结果为undefined。
- 调用函数时,传入的参数可以比接收的参数多,也可以少,也可以不传
// 定义函数
function asd(){...}
var asd = function(){};
function abs(x){
if(indexOf(x) !== 'number'){ // 如果参数不是数字,抛出异常
throw 'Not a number!';
}
if(x >= 0){
return x;
}else{ // 如果是负数,返回正数
return -x;
}
}
abs(-9); // 9
arguments
- 只在函数内部起作用
- 永远指向函数的调用者传入的所有参数
- 利用arguments可以获得所有参数,即使函数没有定义参数
function foo(){
if(arguments.length === 0){
return 0;
}
var arg = arguments[0];
return arg >= 0 ? arg : -arg;
}
foo(-10); // 10
rest参数
- ES6引入了rest参数
- 使用:
function and(a, b, ...rest){}
- rest参数接收多余的参数,是一个数组;如果没有多余的参数,rest会是一个空数组
function abc(a, b, c, ...rest){
alert('a='+a); // 1
alert('b='+b); // 2
alert('c='+c); // 3
alert('rest='+rest); // 4,5
}
abc(1,2,3,4,5);
关于return
return { // return的换行书写方式
...
}
变量作用域
- 如果变量在函数体内声明,这个变量的作用域就是整个函数体,函数外不可引用。
- 如果两个函数体内声明了同样的变量,则该变量的作用域为各自的函数体,互不影响。
- 函数A嵌套函数B时,内部的函数可以访问外部函数的变量(B可访问A中的变量),外部不可访问内部。
嵌套的函数出现变量重名时
- 函数再查找变量时,从自身函数开始,由内向外查找。
function a(){
var x = 1;
function b(){
var x = 'a';
alert("b()的x=" + x);
}
alert("a()的x=" + x);
b();
}
变量提升
- 函数会将函数体内的变量提升到函数的顶部,但不会提升变量的赋值
- 请在函数内部首先声明所有变量哟亲~
function a(){
var x = 1;
alert(x+y);
var y = 2;
// 变量提升后的排列:
// var y; 变量提升
// var x = 1;
// alert(x+y);
// y = 2; 不会提升变量的赋值
}
全局作用域
不在任何函数内定义的变量就具有全局作用域。实际上,JavaScript默认有一个全局对象window,全局作用域的变量实际上被绑定到window的一个属性
var name = 'lxf';
alert(name); // 'lxf'
alert(window.name); // 'lxf'
名字空间
- 全局变量会绑定到window上,如果多个文件声明了相同的全局变量会冲突。
- 解决办法:将所有的变量和函数,都绑定到一个全局变量中。
var myGlobal = {};
myGlobal.name = 'lxf';
myGlobal.age = 26;
myGlobal.a = function(){
alert('您好,' + myGlobal.name + ',您今年' + myGlobal.age + '岁');
}
局部作用域
- 由于变量的作用域是函数体,所以无法在for循环等代码块中声明一个局部变量。
- ES6引入了
let
用来定义块级作用域的变量。
function a(){
for(var i=0;i<10;i++){
...
}
i += 10; // var声明的变量的作用域仍是函数体
}
function b(){
for(let i=0;i<10;i++){
alert(i);
}
alert(i); // let声明了一个块级作用域,所以for语句外无法访问,报错Uncaught ReferenceError: i is not defined
}
常量
- ES6引入了新的关键字
const
来声明常量 - 常量不可重新赋值,会报错
-
const
和let
一样,是块级作用域
const a = 1;
a; // 1
a = 2; // Uncaught TypeError: Assignment to constant variable
方法
- 在一个对象中绑定函数,称为这个对象的方法
- 在一个方法内部,this永远指向当前对象
var menu = {
name: '鱼香肉丝',
price: 15,
order: function(num){
var msg = '您购买了' + num + '份,总价为:' + (num * this.price) + '元。';
return msg;
}
}
this
- 以对象的方法的形式调用对象,this指向这个对象
- 对象的方法函数体中的this指向这个对象,方法中的函数的函数体内的this,指向window(strict模式中指向undefined)
- 直接调用函数,this指向window
- strict模式中,函数中的this指向undefined
- 在方法函数的函数体内声明that来捕获this,然后再方法内部的函数中使用that。
function orderMsg(num){
var msg = '您购买了' + num + '份,总价为:' + (num * this.price) + '元。';
return msg;
}
var menu = {
name: '鱼香肉丝',
price: 15,
order: orderMsg
}
menu.order(2); //"您购买了2份,总价为:30元。"
orderMsg(2); // NaN,直接调用函数,this指向的是window
apply和call
- 使用函数本身的apply方法,控制函数中this的指向。
- apply接收两个参数:第一个是需要绑定this的变量;第二个是array,表示要传给函数本身的参数。
- apply和call类似;apply把参数打包成数组后再传入;call把参数按顺序传入
- 普通的函数使用apply/call时,通常将this绑定为null
function getAge() {
var y = new Date().getFullYear();
return y - this.birth;
}
var xiaoming = {
name: '小明',
birth: 1990,
age: getAge
};
xiaoming.age(); // 25
getAge.apply(xiaoming, []); // 25, this指向xiaoming, 参数为空
Math.max.apply(null, [2,3,4]); // 参数打包成数组传入;this绑定为null
Math.max.call(null, 2,3,4); // 按顺序传入参数;this绑定为null
装饰器
- 动态改变函数的行为、重新指向新的函数
var count = 0;
var oldAlert = window.alert;
window.alert = function(){
count += 1;
return oldAlert.apply(null, arguments);
};
alert(1);
alert(2);
count; // 2
高阶函数
- 函数A接收函数B做为参数,这种函数就是高阶函数
function toNum(a){
var num = Number(a);
return num;
}
function a(x,y,z){
return z(x) + z(y);
}
a('1', '2', toNum); // 3
map/reduce
- map方法传入自定义的函数,可以得到新的数组
- reduce接收两个参数,结果和下一个元素
// map()
function pow(x){
return x * x;
}
var arr = [1,2,3,4,5];
arr.map(pow); // [1,4,9,16,25]
// reduce()
function plus(arr){
var val = arr.reduce(function(x,y){
return x + y;
});
return val;
}
var arr = [1,2,3,5,9];
plus(arr); // 20
练习
- 名字首字母大写,其他小写
var arr = ['BoB', 'PaWN','ann','LEE'];
function change(arr){
var newName = arr.map(function(name){
var lower = name.toLowerCase();
var upper = name[0].toUpperCase();
return upper + lower.substring(1);
});
return newName;
}
change(arr);
filter
- 接收一个函数,这个函数会依次作用于每个元素,然后根据返回的是true还是false来决定保留和删除
var arr = [1,2,3,4,5];
var newArr = arr.filter(function(item){
return item % 2 === 0;
});
newArr; // [2,4]
排序sort()
- sort的默认排序方式是将所有元素转换为字符串,再比对ASCII码进行排序,所以直接使用sort会有坑
- sort排序时,会比较x和y:如果x<y返回-1;如果x=y返回0;如果x>y返回1。会根据返回的值做排序
- sort可以接收一个函数做自定义排序
- sort会直接修改原数组
// 字符串按字母顺序排列
var arr = ['yes','app', 'ball'];
arr.sort(function(x,y){
var first = x.toLowerCase(); // 先统一参数的大小写
var last = y.toLowerCase();
if(first < last){
return -1;
} else if(first == last){
return 0;
} else if(first > base){
return 1;
}
}); // ['app', 'ball', 'yes']
闭包
- 函数作为返回值
- 返回函数不要引用任何循环变量,或者后续会发生变化的变量
- 闭包就是携带状态的函数,并且它的状态会完全的对外隐藏起来
function lazy_sum(arr){
var sum = function(){ // sum可以引用lazy_sum的参数和变量
return arr.reduce(function(x,y){
return x + y;
})
}
return sum; // 返回sum函数,每次调用都会返回一个新的函数
}
// 调用
var f = lazy_sum([1,2,3,4,5]);
f(); // 15
// 一定要引用循环变量时:
function count(){
var arr = [];
for(var i=1;i<=3;i++){
arr.push((function(n){
return function(){
return n * n;
}
})(i));
}
return arr;
}
var result = count();
var f1 = result[0];
f1(); // 1
// 计数器
function counter(num){
var x = num || 0;
return {
inc: function(){
x += 1;
return x;
}
}
}
var create = counter();
create.inc(); // 1
var create2 = counter(20);
create2.inc(); // 21
// 求根:
function newPow(n){
return function (num){
return Math.pow(num, n); // num是数,n是次方
}
}
var pow = newPow(2);
pow(3); // 9
箭头函数
- ES6标准新增的一种函数
- 定义使用的一个箭头
-
x => x * x
等于function(x){return x*x}
- 箭头函数相当于匿名函数
- 如果参数不止一个,需要用括号
()
包起来
// 多语句:
x => {
if (x > 0) {
return x * x;
} else {
return - x * x;
}
}
// 多个参数:
(x,y)=> x * x + y * y;
// 无参数:
() => 1
// 可变参数:
(x,y,...rest) => {
var i,
sum=x+y;
for(I=0;i<rest.length;i++){
sum += rest[I];
}
return sum;
}
// 如果要返回一个对象:
x => {{ foo:x }}
this
- 箭头函数内部的this是词法作用域,由上下文决定
- 由于this已经按词法作用域绑定,所以使用
call()
或者apply()
调用箭头函数时,无法对this进行绑定,即传入的第一个参数被忽略
generator
- generator是ES6引入的新的数据类型,看起来像个函数,但能返回多次
- generator由
function*
定义,除了return
语句,还可以用yield
返回多次 - generator的
next()
方法:执行generator代码,每当遇到yield
时,就返回一个对象{value: x, done: false/true}
function* fib(max){
var t,
a = 0,
b = 1,
n = 1;
while(n<max){
yield a;
t = a + b;
a = b;
b = t;
n++;
}
return a;
}
// 调用generator
// 方法1:使用next()
// done:false表示没有执行结束,true表示结束
// 当done是true时,`value`的值就是`return`的值
var f = fib(5);
f.next(); // Object {value: 0, done: false}
f.next(); // Object {value: 1, done: false}
f.next(); // Object {value: 1, done: false}
f.next(); // Object {value: 2, done: false}
f.next(); // Object {value: 3, done: true}
// done为true之后再使用next方法的话:
f.next(); // Object {value: undefined, done: true}
// 方法2:for...of循环迭代,不需要判断done
for (var x of fib(5)){
console.log(x);
}
因为generator可以在执行过程中多次返回,所以它看上去就像一个可以记住执行状态的函数,利用这一点,写一个generator就可以实现需要用面向对象才能实现的功能。
generator还有另一个巨大的好处,就是把异步回调代码变成“同步”代码。