JavaScript

132 阅读33分钟

输出语句

  • alert("aa");
  • document.write("aaa");
  • console.log("aaa");

js编写位置

  • 可以将js代码编写到标签的onclick属性中 <button onclick="alert("a");">点我一下
  • 可以经js代码编写到href属性中,这样当点击超链接时,会执行js代码;<a href="javascript:alert("aaa");">你也点我一下 < /a>
  • 可以将js代码编写到js标签中
  • 可以将js代码编写到外部文件中,然后通过script标签引入。

基本语法

  • js注释

字面量和变量

标识符

数据类型

  • js中一共有六种数据类型
  • String 字符串
  • Number 数值
  • Boolean 布尔值
  • Null 空值
  • Undefined 未定义
  • Object 对象
  • 其中String,Number Boolean Null Undefined属于基本数据类型 而Object属于引用数据类型

字符串

  • 在Js中字符串需要使用引号引起来
  • 单引号和和双引号都可以,但不能混用
  • 在字符串中,我们可以使用\作为转义字符

Number

  • 在js中所有的数值都是Number类型(包括整数和浮点数)
  • 可以使用typeof来检查一个变量的类型 typeof a
  • Number.MAX_VALUE 最大值
  • infinity 正无穷大的
  • NAN not a number
  • Number.MIN_VALUE = 5e-324 大于0的最小正值
  • 如果使用Js进行浮点运算,可能得到一个不精确的结果。所以千万不要使用JS进行对精确度比较高的运算

布尔值

  • Boolean true false

Null

  • null类型的值只有一个,就是null
  • null这个值专门用来表示一个为空的对象

Undefined

  • undefined只有一个 就是undefined

typeof

  • String string
  • Number number
  • Boolean boolean
  • Null Object
  • Undefined undefined

强制类型转换

  • 将一个数据类型转为其他的数据类型
  • 类型 转换主要指,将其他的数据类型转换位String Number Boolean
将其他的数据类型转换为String
  • 方式一:调用被转换数据类型的toString()方法,该方法不会改变原变量,但是null和undefined这两个值没有toString()方法。
  • 方式二:调用String()函数
将其他的数据类型转换为number
  • 调用Number()方法
  • 1.字符串--->数字
    1)纯数字
    2)有字符串--NAN
    3)" "和""--> 0
  • 2 boolean--->数字
    1) true-->1
    2)false-->0
  • 3 Null--->数字
    null-->0
  • 4 Undefined--->数字
    undefined-->0
  • parseInt()
  • parseFloat()
    如果对非String使用parseInt()或parseFloat()它会先将其转换位String然后再操作
将其他的数据类型转换为Boolean
  • 使用Boolean()函数
  • 1 数字--->boolean
    除了0和NAN,其余都是ture
  • 2 String--->boolean
    除了空串其余都是true
  • null--->boolean null-->false
  • undefined--->boolean undefined-->false
  • 对象也会转换为true

算术运算符

+
  • 可以将两个值相加并将结果返回
  • 当对非Numner类型的值进行运算时,会将这些值转换为Number然后再运算
  • 任何值和NaN相加都是NaN
  • 字符串相加会做拼串
  • 任何值和字符串相加都会转换为字符串,并做拼串操作
- * / %

一元运算符

  • 一元运算符,只需要一个操作符
  • 对于非Number类型的值,他会先将转换为Number,然后再运算,可以对一个其他的数据类型使用+,来将其转换为number,他的原理和Number()函数一样

自增和自减

逻辑运算符

  • js中为我们提供了三种逻辑运算符
  • ! & | && ||
  • 如果对非boolean值进行运算,则会将其转换为布尔值,然后再运算,并返回原值

赋值运算符

  • = 可以将符号右侧的值赋值给左侧的变量

关系运算符

  • 通过关系运算符可以比较两个值之间的大小关系,如果关系成立返回ture,如果关系不成立放回false
  • < >= <=

  • 对于非数值进行比较时候,会先转换成数字,然后再进行比较
  • 任何值和NaN进行比较都是false
  • 如果符号两边都是字符串,不会将其转换为数值,会逐位比较

相等运算符

  • ==
  • ===
  • undefined 延伸于null,所以null===undefined为true
  • null==0为false
  • NaN和任何值都不相等,包括他本身
  • isNaN()

条件运算符

  • 三元运算符
    条件表达式?语句1:语句2;

运算符优先级

  • 使用,可以分割多个语句,一般可以声明多个变量的时候使用
  • 先乘除后加减
  • && 优先级 高于||

代码块

  • {}
  • js中的代码块只有分组的作用,没有其他功能

if语句

  • if(条件表达式){语句1}else{语句2}
  • if(条件表达式){语句1}else if(){语句2}else{语句3}

switch

  • switch(条件表达式){
    case 表达式:
    语句...
    break;
    case 表达式:
    语句...
    break;
    default:
    语句...
    break;
    }

while

  • while(条件表达式){
    语句....
    }
  • 1创初始化一个变量
  • 2在循环中设置一个条件表达式
  • 3定义一个更新表达式,每次更新初始化变量 #### do...while

for

  • for(初始化表达式,条件表达式,更新表达式){

}

break和continue

  • break关键字可以用来退出switch或循环语句
  • 不能在if语句中使用break和continue
  • break关键字,会立即终止离他最近的那个循环语句
js 数据类型
  • String 字符串
  • Number 数量
  • Boolean 布尔值
  • Null 空值
  • Undefined 末定义
    • 以上这五种类型属于基本数据类型,以后我们看到的值,只要不是上边的5种,全都是对象,基本数据类型都是单一的值“hello” 123 true,值和值之间没有任何的联系。在JS中表示一个人,如果使用基本类型数据,我们所创建的变量都是独立的,不能成为一个整体
  • Object 对象

对象的简介

  • 对象属于复合的数据类型,在对象中可以保存多个不同的数据类型的属相
  • 对象的分类
    • 内建对象
      1. 由ES标准中定义的对象,在任何的ES的实现中都可以使用
      2. 比如,Match String Numner Boolean Function Object
    • 宿主对象
      1. 由JS的运行环境的对象,目前来讲主要指由浏览器提供的对象
      2. 比如BOM DOM
    • 自定义对象
      1. 由开发人员自己创建的对象
  • 创建对象 使用new关键字调用的函数是构建函数constructor 构建函数是专门来创建对象的函数,使用typeof检查一个对象时,会返回object
在对象中保存的值称为属性
  向对象添加属性
  语法:对象.属性名 = 属性值
  var obj = new Object();
  obj.name = "孙悟空"; 
读取对象中的属性
  语法:对象.属性名
  如果读取对象中没有的属性,不会报错而是返回undefined
修改对象的属性值
  读法:对象.属性名 = 新值
删除对象的属性
  语法:delete 对象.属性名
  
如果使用特殊的属性名 不能采用.的方式来操作
  obj["123"] = 789
  语法:对象["属性名"] = 属性值
  读取也需要采用这种方式。
  语法:对象["属性名"]
  使用p[]这种形式去操作属性,更加的灵活,在[]中可以直接传递一个变量,这样变量值是多少就会读取那个属性
  obj["nihao"] = "你好"
  var n = "nihao";
  obj[n]
  
  属性值
    JS对象的属性值,可以是任意的数据类型
    甚至也可以是一个对象
in运算符 
  通过该运算符可以检查一个对象中是否含有指定的属性,如果有则返回true,没有返回false
  语法:"属性名" in  对象

对象字面量

使用对象字面量,可以在创建对象时,直接指定对象中的属性
语法:{属性名:属性值,属性名:属性值...}
  对象字面量的属性名可以加引号也可以不加,建议不加
  如果要使用上些的特殊的名字,则必须加引号
属性名和值是一组组的名值对结构
  名和值之间使用 ':' 链接,多个名值对之间使用 ',' 隔开
  如果一个属性之后没有其他的属性了,就不要写 ','

函数

  • 函数也是一个对象
  • 函数中可以封装一些功能(代码),在需要的时候可以执行这些功能(代码)
  • 函数可以保存一些代码在需要的时候调用。
创建一个函数对象 
  可以将要封装的代码以字符串的形式传递给构造函数
  var fun = new Function("console.log('hello world')");
  封装到函数的代码不会立即执行
  函数中的代码会在函数调用的时候执行
调用函数
  语法:函数对象()   fun();
  当调用函数时,函数中封装的代码会按照顺序执行

使用函数声明创建一个对象 
  语法:
  function 函数名([形参1,形参2...形参n]){
    语句...
  }
  function fun2(){
      console.log("hello world");
  }
  
使用函数表达式创建一个对象 
  语法:
  var 函数名 = function([形参1,形参2...形参n]){
    语句...
  }

函数的参数

  • 可以在函数()中指定一个或多个形参(形式参数)
  • 多个形参之间使用','隔开,声明形参就相当于在函数内部声明了对应的变量,但是不赋值
  • 在调用函数时,可以在()中指定实参(实际参数)
  • 实参将会赋值给函数中对应的形参
  • 函数不会检查参数的类型和数量

函数的返回值

  • 可以使用return 来设置函数的返回值
  • 语法:return 值
  • return后的值将会作为函数的执行结果返回
  • 如果只写return 或 不写return 返回undefine

实参可以是任何值

for

  • for(var 变量 in 对象){语句...}

立即执行函数

函数定义完,立即被调用,这种函数叫做立即执行函数
立即执行函数往往只会执行一次
(function(){
    alsert("我是一个匿名函数~~~")
})();
(function(a,b){
    alsert("我是一个匿名函数~~~")
})(123,456);

作用域

  • 作用域指一个变量的作用的范围
  • 全局作用域
    • 直接编写在script标签中的JS代码,都在全局作用域
    • 全局作用域在页面打开时创建,在页面关闭时销毁
    • 在全局作用域中有一个全局对象window,它代表的是一个浏览器的窗口,它由浏览器创建我们可以直接使用
    • 在全局作用域中,创建的变量都会作为window对象的属性保存
  • 函数作用域
    • 调用函数时创建函数作用域,函数执行完毕后,函数作用域销毁
    • 每调用一次函数就会创建一个新的函数作用域,他们之间是互相独立的。
    • 在函数作用域中可以访问全局变量。在全局作用域中不能访问函数作用的变量。
    • 当在函数作用域操作一个变量时,它会先在自身作用域中寻找,如果有就直接使用,如果没有则向上一级作用域中寻找,直接找到全局作用域,如果使用作用域中依然没有找到 ,则会报错
    • 在函数中使用全局作用域,直接使用window

变量的声明提前

  • 使用var关键字声明的变量,会在所有的代码执行之前被声明。
  • 但是如果声明变量是不使用var关键字,则变量不会被声明提前

函数的声明提前

  • 使用函数声明形式创建的函数
    function 函数名(){}
    它会在所有的代码执行之前就会创建,所以我们可以在函数声明前来调用函数。
  • 使用函数表达是创建的函数,不会被声明提前。

this

  • 解析器在调用函数每次都会向内部传递一个隐含的参数,这个隐含参数就是this,this指向的是一个对象,这个对象我们称之为函数执行的上下文对象,根据函数的调用方式不同,this会指向不同的对象。
  • 以函数的方式调用时,this永远都是window
  • 以方法的形式调用时,this就是调用方法的对象。

工厂方法创建函数

  • 使用工厂方法创建的对象,使用的构造函数都是Object
  • 所以创建的对象都是Object这个类型的
  • 就导致我们无法区分多种不同类型的对象

构造函数

  • 创建一个构造函数,专门用来创建Person对象的
  • 构造函数就是一个普通的函数,创建方式和普通函数没有区别
  • 构造函数和普通函数的区别就是调用方式的不同
  • 普通函数是直接调用,而构造函数需要使用new关键字来调用
  • 构造函数的执行流程
    • 1.立刻创建一个新的对象
    • 2.将新建的对象设置为函数中的this
    • 3.逐行执行函数中的代码
    • 4.将新建的对象作为返回值
  • 使用同一个构造函数创建的对象,我们称之为一类对象,也将一个构造函数成为一个类,我们将通过一个构造函数创建的对象称之为该类的实列。
  • 使用instanceof可以检查一个对象是否是一个类的实例。语法: 对象 instanceof 构造函数 如果是返回true,否则返回false

this的情况

  • 当以函数的形式调用时,this是window
  • 当以方法的形式调用时,谁调用方法 this就是谁
  • 当以构造函数的形式调用时,this就是新创建的那个对象

创建一个Person构造函数
  在Person构造函数中,为每一个对象都添加一个sayName方法,目前我们的方法在构造函数内部创建的,也就是构造函数每执行一次就会创建一个新的sayName方法,也就是所有实例都有sayName方法是唯一的,这样就导致了构造函数执行一次就会创建一个新的方法,执行10000次就会创建10000个一模一样的方法
function Person(name,age,gender){
    this.name = name;
    this.age = age;
    this.gender = gender;
    //向对象中添加一个方法
    this.sayName = function(){
        alert("Hello大家好,我是: "+this.name );
    };
    //创建一个Person的实例
    var per = new Person("孙悟空",18,"男");
    var per2 = new Person("猪八戒",19,"女");
}

原型

  • 我们所创建的每一个函数,解析器都会向函数中添加一个属性prototype,这个属性对应着一个对象,这个对象就是我们所谓的原型对象
  • 如果函数作为普通函数调用protorype没有任何作用,当函数通过构造函数形式调用时,它所创建的对象都会有一个隐含的属性,指向该函数的原型对象,我们可以通过__proto__来访问该属性
  • 原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将对象中共有的内容,统一设置到原型对象中
  • 当我们访问对象的一个属性或方法时,它会先在对象自身中寻找,如果有则直接使用,如果没有则会去原型中寻找,如果找到则直接使用
  • 以后我们创建构造函数时,可以将这些对象共有的属性和方法,统一添加到构造函数的原型对象中,这样不用分别为每一个对象添加,也不会影响到全局作用域,就可以使用每个对象都具有这些属性和方法了
  • 原型对象也是对象,所以它也有原型
    • 当我们使用一个对象的属性或方法时,会先在自身中寻找
    • 自身中如果有,则直接使用
    • 如果没有则去原型对象中寻找,如果原型对象中有,则使用
    • 如果不有则去原型的原型中寻找,直到找到Object对象的原型
    • Objec对象的原型没有原型,如果在Object中依然没有找到,则返回undefined

toString()

  • 当我们直接在页面中打印一个对象时,实际上是输出的对象的toString()方法的返回值
  • 如果我们希望输出对象时不输出[Object Object],可以为对象添加一个toString()方法

数组(Array)

  • 数组也是一个对象
  • 它和我们普通对象功能类似,也是用来存储一些值的
  • 不同的是普通对象是使用字符串作为属性名的,而数组使用数字来作为索引操作元素
//创建对象
var arr = new Array();
//使用typeof检查一个数组时,会返回object
//console.log(typeof arr) //object

向数组中添加元素
  语法:数组[索引] = 值
  arr[0] = 10;
  arr[1] = 33;
读取数组中的元素
  语法:数组[索引]
  如果读取不存在的索引,他不会报错而是返回undefined
  console.log(arr[0]);
获取数组的长度
  语法:数组.length
  console.log(arr.length);
  对于连续的数组,使用length可以获取到数组的长度(元素个数)
  对于非连续的数组,使用length会获取到数组的最大的索引加一
  尽量不要创建非连续的数组
  
  使用字面量来创建数组,可以在创建时就指定数组中的元素
  语法:[]
  var arr = [];
  
  使用构造函数创建数组时,也可以同时添加元素,将要添加的元素作为构造函数的参数传递
  元素之间使用 ',' 隔开
  var arr2 = new Array(1,2,3);
  
  //创建一个数组中只有一个元素10
  arr = [10];
  //创建一个长度为10的数组
  arr2 = new Array(10);
  console.log(arr2.length);
  
  数组其他方法查看文档
  插入,删除,遍历
  
  forEach() 方法需要一个函数作为参数
  像这种函数,由我人创建但是不由我们调用的,我们称为回调函数
  数组中有几个元素函数就会执行几次,
  每次执行时,浏览器会将遍历到的元素以实参的形式传递进来
  我们可以定义形参,来读取这些内容
  浏览器会在回调函数中传递三个参数
    第一个参数,就是当前正在遍历的元素
    第二个参数,就是当前正在遍历的索引
    第三个参数,就是正在遍历的数组
  arr.forEach(function(value,index,obj){console.log()});
  forEach() 这个方法只支持IE8以上的浏览器

call()和apply()

  • 这两个方法都是函数对象的方法,需要通过函数对象来调用
  • 当对函数调用call()和apply()都会调用函数执行
  • 在调用call()和apply()可以将一个对象指定为第一个参数
    • 此时这个对象将会成为函数执行时的this
  • call()方法可以将实参在对象之后依次传递
  • apply()方法需要将实参封装到一个数组中统一传递
  • this的情况
    • 1.以函数形式调用时,this永远都是windown
    • 2.以方法形式调用时,this是调用方法的对象
    • 3.以构造函数形式调用时,this是新创建的个对象
    • 4.使用call和apply调用时,this是指定的那个对象
fun.call();
fun.apply();
fun();

arguments

  • 在调用函数进,浏览器每次都会传递进两个隐含的参数
    • 函数的上下文对象this
    • 封装实参的对象arguments
      • arguments是一个类数组对象,它也可以通过索引来操作数据,也可以获取长度
      • 我们即使不定义形参,也可以通过arguments来使用实参
        • 只不过比较麻烦
        • arguments[0] 表示第一个参数
        • arguments[0] 表示第二个参数
      • 它里边有一个属性叫做callee
        • 这个属性对应一个函数对象,就是当前正在执行的函数的对象

Date

Math

包装类

  • 基本数据类型
    • String
    • Number
    • Boolean
    • Null
    • Undefined
  • 引用数据类型
    • Object
  • 在JS中为我们提供了三个包装类,通过这三个包装类可以将基本数据类型的数据转换为对象
    • String()
      • 可以将基本数据类型字符串转换为String对象
    • Number()
      • 可以将基本数据类型的数据转换为Number对象
    • Boolean()
      • 可以将基本数据类型的布尔值转换为Boolean对象
    • 但是注意:我们在实际应用中不会使用基本数据类型的对象
      • 如果使用基本数据类型的对象,在做一些比较时可能带来一些不 可预期的结果
  • 方法和属性能添加给对象,不能添加给基本数据类型
    • 当我们对一些基本数据类型的值去调用属性和方法时,浏览器会临时使用包装类将其转换为对象,然后在调用对象的属性和方法,调用完以后,在将其转换为基本数据类型
var s = 123;
s = s.toString();
s.hello = "你好";
console.log(hello); //undefined

字符串

正则

DOM

  • DOM,全称Document Object Model文档对象模型
  • js中通过DOM来对文档进行操作,只要理解了DOM就可以随心所欲的操作WEB页面
  • 文档表示的就是整个HTML见面文档
  • 对象表示将网页中的每一个部分都转换为一个对象
  • 使用模型来表示对象之间的关系,这样方便我们获取对象

节点

  • 节点Node,是构成我们网页的最基本的组成部分,网页中的每一个部分都可以称为是一个节点
  • 比如:html标签、属性、文本、注释、整个文档都是一个节点
  • 虽然都是节点,但是实际上他们的具体类型是不同的
  • 比如:标签我们称为元素节点、属性称为属性节点、文本称为文本节点、文档称为文档节点
  • 节点的类型不同,属性和方法也都不尽相同
  • 节点:Node-构成HTML文档最基本的单元
  • 常用节点分为四类
    • 文档节点:整个HTML文档
    • 元素节点:HTML文档中的HTML标签
    • 属性节点:元素的属性
    • 文本节点:HTLM标签中的文本内容
---nodeNamenodeTypenodeVlue
文档节点#document9null
元素节点标签名1null
属性节点属性名2属性值
文本节点#text3文本内容
  • 元素节点(Element)
    • HTML中的各种标签都是元素节点,这也是我们最常用的 一个节点
    • 浏览器会将页面中所有的标签都会转换为一个元素节点
    • 我们只可以通过document的方法来获取元素节点
    • 比如
      • document.getElementById()
      • 根据id属性值获取一个元素节点对象
  • 属性节点(Attr)
    • 属性节点表示的是标签中的一个一个的属性,这里要注意的是属性节点并非是元素节点的子节点,而是元素节点的一部分。
    • 可以通过元素节点来获取指定的属性节点
    • 例如:
      • 元素节点.getAttributeNode("属性名")
      • 注意:我们一般不使用属性节点
  • 浏览器已经为我们提供文档节点对象,这个对象就是window属性
  • 可以在页面中直接使用,文档节点代表的是整个页面

事件

  • 事件,就是文档或浏览器窗口中发生的一些特定的交互瞬间
  • JavaSript与HTML之间的交互是通过事件实现的
  • 对于Web应用来说,有下面这些代表性的事件:点击某个元素、将鼠标移动至某个元素上方、按下键盘上某个键,等等。

文档的加载

  • 浏览器在加载一个页面时,是按照自上向下的顺序加载的,读取到一行就 运行一行,如果将script标签写到页面的上边,在代码执行时,页面还没有加载
  • onload事件会在整个页面加载完成之后才触发

JS修改元素的样式(内联样式)

  • 语法:元素.style.样式名 = 样式值
  • 注意:如果CSS的样式名中含有-,这种名称在JS中是不合法的,比如background-color,需要将这种样式名修改为驼峰命名法,去掉-然后将-后面的字母大写
  • 我们通过style属性设置的样式都是内联样式,而内联样式有较高的优先级,所以通过JS修改的样式往往立即显示
  • 但是如果在样式中写!important,则此时样式会有最高的优先级,即使通过JS也不能覆盖该样式,此时会导致JS修改样式失效
  • 读取样式:语法:元素.style.width
  • 通过style属性设置和读取的都是内联样式,无法读取样式表中的样式

读取元素当前的样式

  • 读法:元素.currentStyle.样式名
  • 它可以用来读取当前元素正在显示的样式,如果当前没有设置该样式,则获取它的默认值
  • currentStyle只有IE浏览器支持,其他浏览器都不支持
  • 在其他浏览器中可以使用:getComputedStyle()这个方法获取元素当前的样式,这个方法是window的方法,可以直接使用。
  • 需要两个参数
    • 第一个:要获取样式的元素
    • 第二个:可以传递一个伪元素,一般都是null

事件对象

  • onmousemove
    • 该事件将会在鼠标在元素中移动时被触发
  • 事件对象
    • 当事件的响应函数被触发时,浏览器每次都会将一个事件对象作为实参传递响应函数(IE8作为window属性保存)
    • 在事件对象中封装了当前事件相关的一切信息,比如:鼠标的坐标 键盘哪个键被按下,鼠标滚轮滚动的方向...

事件的冒泡

  • 所谓的冒泡指的就是事件的向上传导,当后代元素上的事件触发时,其祖先元素的相同事件也会被触发
  • 在开发中大部分情况冒泡是有用的,如果不希望发生事件冒泡可以通过事件对象来取消冒泡 event.cancelBubble = true;

事件的委派

  • 指将事件统一绑定给元素的共同的祖先元素,这样当后代元素上的事件触发时,会一直冒泡到祖先元素,从而通过祖先元素的响应函数来处理事件
  • 事件委派是利用了冒泡,通过委派可以减少事件绑定的次数,提高程序的性能

事件的绑定

  • 使用对象.事件 = 函数形式的绑定响应函数
    • 它只能同时为一个元素的一个事件绑定一个响应函数
    • 不能绑定多个,如果绑定了多个,则后边的会覆盖掉前面的
  • addEventListener()
    • 通过这个方法也可以为元素绑定响应函数
    • 参数:
      • 1.事件的字符串,不要on
      • 2.回调函数,当事件触发时,该函数会被调用
      • 3.是否在捕获时触发,需要一个布尔值,一般都传fasle
    • 使用addEventListener()可以同时为一个元素的相同事件同时绑定多个响应函数
    • 这样当事件被触发时,响应函数会按照函数的绑定顺序执行
    • 这种方法不支持IE8(attachEvent() 1.事件的字符串,要on 2.回调函数)
      • 这个方法也可以同时为一个事件绑定多个处理函数
      • 不同的是它是后绑定先执行,执行顺序和addEventListener()相反

完成bind函数

  • 定义一个函数,用来为指定元素绑定响应函数
  • addEventListener()中的this,是绑定事件的对象
  • attachEvent()中的this,是window
  • 需要统一两个方法this
  • 参数
    • obj 要绑定事件的对象
    • eventStr 事件的字符串(不要on)
    • callback 回调奇函数
function bind(obj,enentStr,callback){
    if(obj.addEventListener){
        //大部分浏览器兼容的方式
        obj.addEventListener(eventStr,callback,false);
    }else{
        //IE8及以下
        this是由调用方式决定
        callback.call(obj);
        obj.attachEvent("on"+eventStr,function(){
            //在匿名函数中调用回调函数
            callback.call(obj);
        });
    }
}

事件的传播

  • 关于事件的传播网景和微软公司有不同的理解
  • 微软公司认为事件应该是由内向外传播,也就是当前事件触发时,应该先触发当前元素上的事件,然后再向当前元素的祖先元素上传播,也就是事件应该在冒泡阶段执行
  • 网景公司认为事件应该是由外向内传播的,也就是当前事件触发时,应该先触发当前元素的层的祖先元素的事件,然后在向内传播后代元素
  • w3c综合了两个公司的方案,将事件传播分成了三个阶段
    • 捕获阶段
      • 在捕获阶段时从最外层的祖先元素,向目标元素进行事件的捕获,但是默认此时不会触发事件
    • 目标阶段
      • 事件捕获到目标元素,捕获结束开始在目标元素上触发事件
    • 冒泡阶段
      • 事件从目标元素向他的祖先元素传递,依次触发祖先元素上的事件
  • 如果希望在捕获阶段就触发事件,可以将addEventListener()的第三个参数设置为true
  • 一般情况下我们不会希望在捕获阶段触发事件,所以这个参数一般都是false
  • IE8及以下的浏览器中没有捕获阶段

鼠标滚轮事件

键盘事件

BOM

  • 浏览器对象模型
  • BOM可以使我们通过JS来操作浏览器
  • 在BOM中为我们提供了一组对象,用来完成对浏览器的操作
  • BOM对象
    • window
      • 代表的是整个浏览器的窗口,同时window也是网页中的全局对象
    • Navigator
      • 代表的当前浏览器的信息,通过该对象可以用来识别不同的浏览器
      • 由于历史原因,Navigator对象中的大部分属性都已经不能帮助我们识别不同的浏览器
      • 一般我们只会使用userAgent来判断浏览器信息,userAgent是一个字符串,这个字符串中包含有用来描述浏览器信息的内容,不同的浏览器会有不同的userAgent
      • 在IE11中已经将微软和IE相关的标识都已经去除了,所以我们基本已经不能通过UserAgent来识别一个浏览器是否是IE了
      • 如果通过UserAgent不能判断,还可以通过一些浏览器中特有的对象,来判断浏览器的信息,比如:ActiveXObject
    • Location
      • 代表当前浏览器的地址栏信息,通过Location可以获取地址栏信息或者操作浏览器跳转页面
      • 如果直接打印location,则可以获取到地址栏的信息(当前页面的完整路径)
      • 如果直接将location属性悠为一个完整的路径,或相对路径,则我们页面会自动跳转到该路径,并且会生成相应的历史记录
      • assign()
        • 用来跳转到其他页面,作用和直接修改location一样
      • reload()
        • 用来重新加载当前页面,作用和刷新按钮一样
        • 如果在方法中传递一个true,作为参数,则会强制清空缓存刷新页面
      • replace()
        • 可以使用一个新的页面替换当前页面,调用完毕也会跳转页面
        • 不会生成历史记录
    • History
      • 代表浏览器的历史记录,可以通过该对象来操作浏览器的历史记录
      • 由于隐私原因,该对象不能获取到具体的历史记录,只能操作浏览器向前或 向后翻页,而且该操作只在当次访问时有效
      • length
        • 属性,可以获取到当成访问的链接数量
        • back() forward() go()
    • Screen
      • 代表用户屏幕的信息,通过该对象可以获取到用户显示器相关的信息
  • 这些BOM对象在浏览器中都是作为window对象的属性保存
  • 可以通过window对象来使用,也可以直接使用

定时器

setInterval()
定时调用
可以将一个函数,每隔一段时间执行一次
参数
  1.回调函数,该函数会每隔一段时间被调用一次
  2.每次调用间隔的时间,单位是毫秒
返回值
  返回一个Number类型的数据
  这个数字用来作为定时器的唯一标识

setInterval(function(){},1000);
clearInterval() 
可以用来关闭一个定时器
方法中需要一个定时器的标识作为参数,这样将关闭标识对应的定时器

IIFE

  • 理解
    • 全称:Immediately-Invoked Function Expressioin
  • 作用
    • 隐藏实现
    • 不会污染外部(全局)命名空间
(function(){ //匿名函数自调用
    console.log('...');
})();

函数的prototype属性

  • 每个函数都有一个prototype属性,它默认指向一个Object空对象(即称为:原型对象)
  • 原型对象中有一个属性constructor,它指向函数对象
  • 给原型对象添加属性(一般都是方法)
  • 作用:函数的所有实例对象自动拥有原型中的属性(方法)

显示原型与隐式原型

  • 每个函数function都有一个prototype,即显示原型
  • 每个实例对象都有一个__proto__,可称为隐式原型
  • 对象的隐式原型的值为对应构造函数的显式原型的值
  • 内存结构
  • 总结
    • 函数的prototype属性:在定义函数时自动添加的,默认值是一个空Object对象
    • 对象的__proto__属性:创建对象时自动添加的,默认值为构造函数的prototype属性值
    • 程序员能直接操作显示原型,但不能直接操作隐式原型(ES6之前)

原型链

  • 访问一个对象的属性时,先在自身属性中查找,找到返回
  • 如果没有,再沿着__prototype__这条链向上查找的,找到返回
  • 如果最终没有找到,返回undefined
  • 别名:隐式原型链
  • 作用:查找对象的属性(方法)
  • 构造函数/原型/实体对象的关系(图解)
  • 构造函数/原型/实体对象的关系2(图解)
  • 1.函数的的显示原型指向的对象默认是空Object实例对象(但Object不满足)
  • 2.所有函数都是Function的实例(包含Function)
  • 3.Object的原型对象是原型链尽头
  • 1.读取对象的属性时:会自动到原型链中查找
  • 2.设置对象的属性值时,不会查找原型链,如果当前对象中没有此属性,直接添加此属性并设置其值
  • 3.方法一般定义在原型中,属性一般通过构造函数定义在对象本身上

instanceof

  • instanceof 是如何判断的?
  • 表达示:A instanceof B
  • 如果B函数的显示原型对象在A对象的原型链上,返回true,否则返回false
  • Function是通过new自己产生的实例

变量与函数提升

  • 1.变量声明提升
    • 通过var定义(声明)的变量,在定义语句之前就可以访问到
    • 值:undefined
  • 2.函数提升
    • 通过function 声明的函数,在之前就可以直接调用
    • 值:函数定义(对象)
  • 问题:变量提升和函数提升是如果产生的

执行上下文

    1. 代码分类(位置)
    • 全局代码
    • 函数代码
    1. 全局执行上下文
    • 在执行全局代码前将window确定为全局执行上下文
    • 对全局数据进行预处理
      • var定义的全局变量==> undefined,添加为window的属性
      • function声明的全局函数==>赋值(fun),添加为window的方法
      • this==>赋值(window)
    1. 函数执行上下文
    • 在调用函数,准备执行函数体之前,创建对应的函数执行上下文对象
    • 对局部数据进行预处理
      • 形参变量==>赋值(实参)==>添加为执行上下文的属性
      • arguments==>赋值(实参列表),添加为执行上下文的属性
      • var定义的局部变量-->undefined,添加为执行上下文的属性
      • function声明的函数==>赋值(fun),添加执行上下文的方法
      • this==>赋值(调用函数的对象)
    • 开始执行函数体代码

执行上下文栈

  • 1.在全局代码执行前,JS引擎就会创建一个栈来存储管理所有的执行上下文对象
  • 2.在全局执行上下文(window)确定后,将其添加到栈中(压栈)
  • 3.在函数执行上下文创建后,将其添加到栈中(压栈)
  • 4.在当前函数执行完后,将栈顶的对象移除(出栈)
  • 5.当所有的代码执行完后,栈中只剩下window

作用域

闭包

    1. 如何产生闭包
    • 当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时,就产生了闭包
    1. 闭包到底是什么?
    • 使用chrome调试查看
    • 理解一:闭包是嵌套的内部函数(绝大部分人)
    • 理解二:包含被引用变量(函数)的对象(极少数人)
    • 注意:闭包存在于嵌套的内部函数中
  • 产生闭包的条件
    • 函数嵌套
    • 内部函数引用了外部函数的数据(变量/函数)

常见的闭包

  • 1.将函数作为另一个函数的返回值
  • 2.将函数作为实参传递给另一个函数调用

闭包的作用

  • 1.使用函数内部的变量在函数执行完后,仍存活在内存中(延长了局部变量的生命周期)
  • 2.让函数外部可以操作(读写)到函数内部的数据(变量/函数)
  • 问题:
    • 函数执行完后,函数内部声明的局部声明的局部变量是否还存在?
      • 一般是不存在,存在于闭包中的变量才可能存在
    • 在函数外部能直接访问函数内部的局部变量吗?
      • 不能,但是我们可以通过闭包让外部操作它

闭包的生命周期

  • 产生:在嵌套内部函数定义执行完时就产生了(不是调用)
  • 死亡:在嵌套的内部函数成为垃圾对象时

优缺点

  • 缺点
    • 函数执行完后,函数内的局部变量没有释放,占用内存时间会变长
    • 容易造成内存泄露
  • 解决
    • 能不用闭包就不用
    • 及时释放
  • 内存溢出
    • 一种程序运行出现的错误
    • 当程序运行需要的内存超过了剩余的内存时,就出抛出内存溢出的错误
  • 内存泄露
    • 占用的内存没有及时释放
    • 内存泄露积累多了就容易导致内存溢出
    • 常见的内存泄露
      • 意外的全局变量
      • 没有及时清理的计时器或回调函数
      • 闭包

对象创建模式

  • 方式一:Object构造函数模式
    • 套路:创建空Object对象
    • 适用场景:起始时不确定对象内部数据
    • 问题:语句太多
  • 方式二:对象字面量模式
    • 套路:使用{}创建对象,同时指定属性/方法
    • 适用场景:起始时对象内部数据是确定的
    • 问题:如果创建多个对象,重复代码
  • 方式三:工厂模式
    • 套路:通过工厂函数动态创建对象并返回
    • 适用场景:需要创建多个对象
    • 问题:对象没有一个具体的类型,都是Object类型
  • 方式四:自定义构造函数模式
    • 套路:自定义构造函数,通过new创建对象
    • 适用场景:需要创建多个类型确定的对象
    • 问题:每个对象都有相同的数据,浪费内存
  • 方式五: 构造函数+原型的组合模式
    • 套路:自定义构造函数,属性在函数中初始化,方法添加到原型上
    • 适用场景:需要创建多个类型确定的对象