Javascript教程笔记

一.快速入门

  1. 代码使用方法:(1)放在< head>中,在< script>…< script>包含的代码即为
    JavaScript代码
    (2)把JavaScript放在单独的.js文件,然后再HTML中引入文
  2. 如何运行JavaScript?以file://开头的地址无法执行如联网等JavaScript代码
    你还是需要架设一个Web服务器,然后以http://开头的地址来正常
    
    执行所有JavaScript代码
  3. 如何调试JavaScript?。。。Chrome用不了啊。。。。悲伤
  4. 注释:以//开头直到行末的字符被视为行注释,或者//
  5. 大小写:区分大小写
  6. 数据结构:(1)Number:
    (2)布尔值
     (3)字符串:多行字符串(`   `使用反引号)
                  连接字符串(用${ },也可以用+)
                 字符串不可变
                 返回不用的字符串,实际上并不改变原来的字符串:toUpperCase,toLowerCase,indexOf,substring
    
  7. 运算:(1)比较运算:==比较,它会自动转换数据类型再比较,很多时候,
    会得到非常诡异的结果;===比较,它不会自动转换数据类型,
    如果数据类型不一致,返回false,如果一致,再比较
    注意:NaN与任何数值都不相同,包括自己,只能使用isNaN()
         比较浮点数:注意计算机会产生小数点
          null表示一个空的值,而undefined表示值未定义
    
  8. 数组:(1)[]或者Array,
    (2)Array如果通过索引赋值时,索引超过了范围,同样会引起Array
    大小的变化
    (3)相关函数:slice()截取Array的部分元素,返回一个新的Array
                  Unshift(),shift():如果要往Array的头部添加若干元素,
    
    使用unshift()方法,shift()方法则把
    Array的第一个元素删掉
  9. 对象:键-值组成的无序集合(动态类型)
    object.prop或者xiaohong['middle-school'](单引号)
    添加或者删除:xiaoming.age; // undefined
    
    xiaoming.age = 18; // 新增一个age属性
    xiaoming.age; // 18
    delete xiaoming.age;//删除属性
    判断属性是否存在:in(要判断一个属性是否是xiaoming自身拥有的,
    
    而不是继承得到的,可以用hasOwnProperty()方法)
    for (var key in o) {
    if (o.hasOwnProperty(key)) {
    alert(key); // ‘name’, ‘age’, ‘city’
    }
    }
  10. 变量:动态变量:var(一个变量名称只能用var声明一次,用var声明后,可
    以不用var再定义一个名称相同的变量,前后的数据类型
    可以不痛)
    静态变量:int等定义,前后数据类型必须相同

  11. strict模式:强制性用var定义,不用var定义就会是全局变量,会在同一个页
    面的不同的JavaScript文件中出错,未使用var申明变量就使用
    的,将导致运行错误
    ‘use strict’;

  12. Map和Set:(1)Map:Map是一组键值对的结构
    (2)Set和Map类似,也是一组key的集合,但不存储value
    
  13. Array、Map和Set都属于iterable类型。
    为什么要使用for……of?和for…..in有什么区别?
    for … of循环则完全修复了这些问题,它只循环集合本身的元素

    1
    2
    3
    4
    5
    6
    7
    var a = ['A', 'B', 'C'];
    a.name = 'Hello';for (var x in a) {
    a.name = 'Hello';for (var x of a) {
    alert(x); // '0', '1', '2', 'name'
    alert(x); // 'A', 'B', 'C'
    }
    }
  14. iterable类型,在Array,Set,Map等都可以使用函数forEach

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    var a = ['A', 'B', 'C'];
    a.forEach(function (element, index, array) {
    // element: 指向当前元素的值
    // index: 指向当前索引
    // array: 指向Array对象本身
    alert(element);
    });
    var s = new Set(['A', 'B', 'C']);
    s.forEach(function (element, sameElement, set) {
    alert(element);
    });
    var m = new Map([[1, 'x'], [2, 'y'], [3, 'z']]);
    m.forEach(function (value, key, map) {
    alert(value);
    });

二.函数

  1. arguments:它只在函数内部起作用,并且永远指向当前函数的调用者传入的所有参数,(常用于判断参数的个数)
  2. rest参数:arguments中多余的参数,无则为“空数组”
  3. 如果内部函数和外部函数的变量名重名怎么办?
    JavaScript的函数在查找变量时从自身函数定义开始,从
    “内”向“外”查找
  4. 引擎自动提升了变量y的声明,但不会提升变量y的赋值
  5. 全局作用域:变量:默认有一个全局对象window,全局作用域的
    (只有一个) 变量实际上被绑定到window的一个属性
    函数:foo(); // 直接调用foo()
    
    window.foo(); // 通过window.foo()调用
    windows.foo都可以当成是全局变量
  6. 名字空间:不同的JavaScript文件如果使用了相同的全局变量,
    或者定义了相同名字的顶层函数,都会造成命名冲
    突,并且很难被发现。
    减少冲突的一个方法是把自己的所有变量和函数全部绑定到一个全局变量中。例如:
    // 唯一的全局变量MYAPP:
    var MYAPP = {};
  7. 局部作用域:用let替代var可以申明一个块级作用域的变量
    let i=0
    
  8. 常量:关键字const来定义常量,const与let都具有块级作用域
  9. 对象中定义函数(方法):
    1
    2
    3
    4
    5
    6
    7
    8
       var xiaoming = {
    name: '小明',
    birth: 1990,
    age: function () {
    var y = new Date().getFullYear();
    return y - this.birth;
    }
    };

调用方法:(错误)因为有this,不能在外部调用
修改方法:(1)var that = this;

1
2
3
4
5
6
7
8
9
10
11
var xiaoming = {
name: '小明',
birth: 1990,
age: function () {
var that = this; // 在方法内部一开始就捕获this
function getAgeFromBirth() {
var y = new Date().getFullYear();
return y - that.birth; // 用that而不是this
}
return getAgeFromBirth();
}};

(2)用apply修复getAge()调用
getAge.apply(xiaoming, []):第一个参数就是需要
绑定的this变量,第二个参数是
Array,表示函数本身的参数。
(3)call()函数
call()把参数按顺序传入
E.g Math.max.call(null, 3, 5, 4);

  1. 高阶函数:
    (1) map:map()传入的参数是pow,即函数对象本身
    arr.map(pow);
    arr.map(function(str){
    
    return str[0].toUpperCase()+str.slice(1).toLowerCase();
    })
    (2)reduce:sum=arr.reduce(function (x,y){
          return x*y;});
    //运算
    
    (3)filter:arr.filter(function (element, index, self) {
    index=第一个元素所在的位置,所以便于滤掉重复的元素
    区别:返回值为True或者False
    (4)sort()方法也是一个高阶函数,它还可以接收一个比较函数来实现自定义的排序。
  2. 闭包:
    为什么使用闭包?可以定义一个函数,返回值为函数内定义的(内部)
    函数,调用这个函数的时候,内部函数不会立即执
    行,需要var f = lazy_sum([1, 2, 3, 4, 5]);
    f();
    但是因为每次调用互不干涉,返回多个参数时出错
    1
    2
    3
    4
    var results = count();
    var f1 = results[0];//16
    var f2 = results[1];//16
    var f3 = results[2];//16

使用闭包的格式:在内部函数中定义:定义后会立即执行
对:(function (x) {
return x x;
})(3);
错:function (x) { return x
x } (3);
使用闭包的好处:(1)闭包就是携带状态的函数
(2)闭包还可以把多参数的函数变成单参数的函数。例如,要计算x^y可以用Math.pow(x, y)函数,不过考虑到经常计算x2或x3,我们可以利用闭包创建新的函数pow2和pow3:

1
2
3
4
5
6
7
8
function make_pow(n) {
return function (x) {
return Math.pow(x, n);
}
}
// 创建两个新函数:var pow2 = make_pow(2);var pow3 = make_pow(3);
pow2(5); // 25
pow3(7); // 34

经典例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
'use strict'; 
// 定义数字0:
var zero = function (f) {
return function (x) { return x;
}
};
// 定义加法:
function add(n, m) {
return function (f) {
return function (x) {
return m(f)(n(f)(x));
}
}
}
// 计算数字2 = 1 + 1:
var two = add(one, one);

  1. 箭头函数:相当于简化函数

    1
    fn = () => new Date().getFullYear() - this.birth; // this指向obj对象

    修改前面函数中this的问题

  2. generator:返回多个参数
    格式:function* 和yield返回
    调用格式:for……of或者
    f.next()不断调用(每次遇到yield x;就返回一个对象{value: x, done: true/false},然后“暂停”。返回的value就是yield的返回值,done表示这个generator是否已经执行结束了。如果done为true,则value就是return的返回值。)
    定义的函数:比如定义自加的函数,函数内就直接自己加,加到头

三.标准对象

  1. 不要使用new Number()、new Boolean()、new String()创建包装对象
  2. 通常不必把任意类型转换为boolean再判断,因为可以直接写if (myVar) {…}
  3. 函数内部判断某个变量是否存在用typeof myVar === ‘undefined’
  4. Date
    (1)月份范围用整数表示是0~11,0表示一月,1表示二月
    (2)创建一个指定日期和时间的Date对象
    1
    var d = new Date(2015, 5, 19, 20, 15, 30, 123);

第二种创建方法是解析一个符合ISO 8601格式的字符串

1
2
3
4
var d = Date.parse('2015-06-24T19:49:22.875+08:00');
d; // 1435146562875 -------时间戳
var d = new Date(1435146562875);
d; // Wed Jun 24 2015 19:49:22 GMT+0800 (CST)

  1. RegExp(正则表达式)】
    /…./或者new RegExp(‘正则表达式’)
    (1)正则表达式写法:\s 空格
    \w   字母或者数字
    \d   字母
     .    任意字符
    转义(\)   特殊符号
    个数:*为任意个数
          +一个
          {n,m}n-m个字符
          {n}n个字符
    
    (2) 正则进阶:使用[]
    [0-9a-zA-Z_]匹配一个数字、字母或者下划线;
    [0-9a-zA-Z\_]+可以匹配至少由一个数字、字母或者下划线组成的字符串,比如'a100','0_Z','js2015'等等;
    *,{0,19}。。。。。。
    A|B可以匹配A或B
    ^表示行的开头,^\d表示必须以数字开头。
    
    $表示行的结束,\d$表示必须以数字结束
    (3)test()方法用于测试给定的字符串是否符合条件
    (4)用()分组,提取子串的功能
    1
    2
    3
    var re = /^(\d{3})-(\d{3,8})$/;
    re.exec('010-12345'); // ['010-12345', '010', '12345']
    re.exec('010 12345'); // nul

exec()方法在匹配成功后,会返回一个Array,第一个元素是正则表达式匹配到的整个字符串,后面的字符串表示匹配成功的子串。

1
2
var re = /^(\d+?)(0*)$/;
re.exec('102300'); // ['102300', '1023', '00']

?用来表示:取消正则化的贪婪匹配
(5)全局匹配:/…../g 即可以多次执行exec()函数来多次匹配
(6)验证一个Email地址:
var re = /^[a-zA-Z0-9.]+@\w+.\w+$/;(进行分析)
[]内字符之间不需要用+或者其他符号表示不同
后面的+号表示至少匹配一个字符
@不是转义字符,直接用@就好,不用\@
\w+表示@后面的字符
.表示Email的.
\w+表示com、cn等字符
注意添加(),()用来分组,区分不同的字符串

  1. JSON
    (1)字符集必须是UTF-8
    (2)JSON的字符串规定必须用双引号””,Object的键也必须用双引号””
    (3)序列化:使用JSON.stringify(参数1,参数2,参数3)格式
    参数1:对象序列
    参数2:A.Array定义想要显示的键[‘’,‘’....]
             默认为null
           B.传入函数,将每个键值对都会被函数先处理
               (key,value)
    参数3: '  '按缩进输出
    
    另一种方法:精确控制序列化小明,可以给xiaoming定义一个toJSON()的方法,直接返回JSON应该序列化的数据(和属性格式一样)
    1
    2
    3
    4
    5
    6
    toJSON: function () {
    return { // 只输出name和age,并且改变了key:
    'Name': this.name,
    'Age': this.age
    };
    }

(4)反序列化:直接用JSON.parse()把它变成一个JavaScript对象

四.面向对象编程

  1. 不区分类和实例:所有对象都是实例,所谓继承关系不过是把一个对象的原型指向另一个对象而已
  2. 创建对象:(1)xiaoming.proto = Student;
    (Student为var定义)通过原型定义
     (2)方式二:
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
     // 原型对象:var Student = {
    name: 'Robot',
    height: 1.2,
    run: function () {
    console.log(this.name + ' is running...');
    }
    };
    function createStudent(name) {
    // 基于Student原型创建一个新对象:
    var s = Object.create(Student); //用create()方法传入一个原型对象
    // 初始化新对象:
    s.name = name;
    return s;
    }
    var xiaoming = createStudent('小明');
    xiaoming.run(); // 小明 is running...
    xiaoming.__proto__ === Student; // true

new Student创建方法从原型上获取了一个constructor属性,指向Student函数本身

1
2
3
4
5
6
7
8
9
10
11
12
xiaoming ↘
xiaohong -→ Student.prototype ----> Object.prototype ----> null
xiaojun ↗
function Student(name) { //构造函数
this.name = name;
this.hello = function () {
alert('Hello, ' + this.name + '!'); //不需要return
}
}
var xiaoming = new Student('小明'); //使用关键字new
xiaoming.name; // '小明'
xiaoming.hello(); // Hello, 小明!

(4)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function Student(props) {
this.name = props.name || '匿名'; // 默认值为'匿名'
this.grade = props.grade || 1; // 默认值为1
}
Student.prototype.hello = function () {//将hello函数调出
alert('Hello, ' + this.name + '!');
};
function createStudent(props) { //防止忘记写new
return new Student(props || {})
}
var xiaoming = createStudent({
name: '小明'
});
xiaoming.grade; // 1

avatar

  1. 原型继承

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    function inherits(Child,Parent)
    {
    var F=function(){};
    F.prototype=Parent.prototype;
    Child.prototype=new F();
    Child.prototype.constructor=Child;
    }
    function Student(props){//初级定义的对象
    this.name=props.name||’Unnamed’;
    }
    Student.prototype.hello=function(){
    alert(‘Hello,’+this.name+’!’);
    }
    function PrimaryStudent(props){//下一级的定义的对象
    Student.call(this,props);
    this.grade=props.grade||1;
    }
    //实现原型继承链
    Inherits(PrimaryStudent,Student);

    //绑定其他方面到PrimaryStudent原型
    PrimaryStudent.prototype.getGrade=function(){
    return this.grade;
    }
  2. Class继承
    需要一个工具把class代码转换为传统的prototype代码,可以试试Babel这个工具。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    class Student {
    constructor(name) {
    this.name = name;
    }
    hello() {
    alert('Hello, ' + this.name + '!');
    }
    }
    var xiaoming = new Student('小明');
    xiaoming.hello();
    class PrimaryStudent extends Student { //已经继承了hello方法
    constructor(name, grade) {
    super(name); // 记得用super调用父类(Student的props)的构造方法!否则父类的name属性无法正常初始化
    this.grade = grade;
    }
    myGrade() {
    alert('I am at grade ' + this.grade);
    }
    }

五.浏览器

  1. window对象有innerWidth和innerHeight属性,可以获取浏览器窗口的内部宽度和高度 (outerWidth和outerHeight)
  2. document对象表示当前页面。由于HTML在浏览器中以DOM形式表示为树形结构,document对象就是整个DOM树的根节点。
    document的title属性是从HTML文档中的< title>xxx</ title>读取的,但是可以动态改变:
  3. DOM属性:(1)通过两种函数找到DOM节点
    menu = document.getElementById(‘drink-menu’);
    drinks = document.getElementsByTagName(‘dt’);
    (2)document对象还有一个cookie属性
    应该始终坚持使用httpOnly(安全问题)
  4. 操作DOM
    寻找DOM节点:getElementById返回节点唯一
    document.getElementsByTagName()和document.getElementsByClassName()总是返回一组DOM节点
    
    (1)更新DOM
    innerHTML:节点文本内容
    CSS格式:p.style.color = ‘#ff0000’;
    p.style.fontSize = ‘20px’;
    (2)删除DOM
    找到父节点—-调用父节点的removeChild
    删除后则实时更新children属性
    注意删除多个节点时
  5. 操作表单
    (1)文本框,对应的< input type=”text”>,用于输入文本;
    口令框,对应的< input type=”password”>,用于输入口令;
    单选框,对应的< input type=”radio”>,用于选择一项;
    复选框,对应的< input type=”checkbox”>,用于选择多项;
    下拉框,对应的< select>,用于选择一项;
    隐藏文本,对应的< input type=”hidden”>,用户不可见,但表单提交时会把隐藏文本发送到服务器。
    (2)获取值:input.value #输入框
    Input.checked #单选框和复选框,false和true
    (3)提交表单:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <!-- HTML --><form id="login-form" method="post" onsubmit="return checkForm()">
    <input type="text" id="username" name="username">
    <input type="password" id="input-password"> //不定义name属性,表示数据不会被提交
    <input type="hidden" id="md5-password" name="password">//转化为MD5类型
    <button type="submit">Submit</button></form>
    <script>function checkForm() {//定义checkForm()函数
    var input_pwd = document.getElementById('input-password');
    var md5_pwd = document.getElementById('md5-password');
    // 把用户输入的明文变为MD5:
    md5_pwd.value = toMD5(input_pwd.value);
    // 继续下一步:
    return true;
    }</script>

(4)提交文件:
< input type=”file”> 选择本地文件
当一个表单包含< input type=”file”>时,表单的enctype必须指定为multipart/form-data,method必须指定为post,浏览器才能正确编码并以multipart/form-data格式发送表单的数据。