廖雪峰的javascript教程笔记

入门

数据类型和变量

数据类型

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来声明常量
  • 常量不可重新赋值,会报错
  • constlet一样,是块级作用域
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还有另一个巨大的好处,就是把异步回调代码变成“同步”代码。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,602评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,442评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,878评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,306评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,330评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,071评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,382评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,006评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,512评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,965评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,094评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,732评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,283评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,286评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,512评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,536评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,828评论 2 345

推荐阅读更多精彩内容