java script

 

  1. HTML 定义了网页的内容
  2. CSS 描述了网页的布局
  3. JavaScript 网页的行为

 

1:什么是java script

 

  广义的JavaScript=ECMAScript(js的核心语法)+DOM(专门操作网页内容的API)+BOM(专门操作浏览器窗口的API)

    DOM:基于ES标准制定的专门操作网页内容的API。

    API:浏览器已经实现的,开发人员直接使用的编程接口。

    BOM:专门操作浏览器窗口的API;没有标准,各浏览器自行定义。

 

特点:

    1.代码可使用任何文本编辑工具编写,语法类似于C和Java。

    2.无需编译,有JavaScript引擎解释执行。

    3.弱类型语言

    4.基于对象

2:javaScript的四种引入方式

1)直接在HTML页面中使用<script></script>标签引入,可以写在head中,也可以写在body末尾,可以实现

2).在html中使用script的src属性引入外部js文件,可实现。

 

<script src="index1.js">  </script>  

3).事件定义,直接在组件后面的事件中写javascript:js代码,可实现

 

<button id="button" onclick="javascript:alert('js引入的方式3:事件定义')">实验</button> 

4).在一个js文件中调用另外一个js 文件,可实现。但不能直接在第一个js文件中写<script src=xx.js></script>,这样写会导致引入js文件失败

 

<script src="index1-1.js">  
        </script>  

3:JS中的六大数据类型

五种基本数据类型(Number,String,Boolean,Undefined,Null),和一种复杂数据类型(Object)。

 

默认为 false 的值:undefined,null,””,NaN,0,false

1.Number类型

 

Number类型包含整数和浮点数(浮点数数值必须包含一个小数点,且小数点后面至少有一位数字)两种值。

NaN:非数字类型。特点:① 涉及到的 任何关于NaN的操作,都会返回NaN   ② NaN不等于自身。

isNaN() 函数用于检查其参数是否是非数字值。

isNaN(123)  //false   isNaN("hello")  //true

2.String类型

字符串有length属性。

字符串转换:转型函数String(),适用于任何数据类型(null,undefined 转换后为null和undefined);toString()方法(null,defined没有toString()方法)。

3.Boolean类型

该类型只有两个值,true和false

4.Undefined类型

只有一个值,即undefined值。使用var声明了变量,但未给变量初始化值,那么这个变量的值就是undefined。

5.Null类型

null类型被看做空对象指针,前文说到null类型也是空的对象引用。

6.Object类型

js中对象是一组属性与方法的集合。这里就要说到引用类型了,引用类型是一种数据结构,用于将数据和功能组织在一起。引用类型有时候也被称为对象定义,因为它们描述的是一类对象所具有的属性和方法。

4:预编译

1.创建 AO 对象。
2.寻找形参和变量声明,将变量和形参作为AO对象的属性名添加到对象中,值为undefined。值得注意的是,函数声明不叫变量。
3.将实参值和形参值相统一。
4.在函数体里面寻找函数声明,将函数名作为属性名,值为这个函数的函数体。

5:创建一个对象(4种方法):

1.对象字面量:var obj = {};
2.构造函数:var obj = new Object();
3.自定义构造函数:function Obj() {}; var obj = new Obj();
4.Object.create:var obj = Object.create({});

对象的增删改查:
1.增 obj.xxx = xxx;
2.改 obj.xxx = xxx;
3.查 obj.xxx
3.删 delete obj.xxx;

6:原型(prototype) & 原型链

 

原型定义: 原型是 function 对象的一个属性,它是构造函数制造出来的对象的公共祖先。通过这个构造函数产生的对象,可以继承该原型的属性和方法。原型也是对象。

构造函数可以通过 function.prototype = {}; 来 添加 / 修改 原型上的属性
对象可以通过 object.proto = {}; 来 添加 / 修改 原型上的属性

注意:undefined,null 没有原型

原型链定义: 原型的对象还有自己的原型,这样原型上还有原型的结构就构成了原型链。

   Father.prototype = {
       name:'lll'
   }
   function Father(){
       this.name = "qian";
       this.age = "66";
   }
   Son.prototype = new Father();
function Son(){
    this.heigth = '111';

}
var son = new Son;
console.log(son)

7:构造函数中 new 操作符干了什么?

1.在构造函数中,隐士的创建一个为 this 的空对象 var this = {};
2.在构造函数中,为 this 这个空对象添加属性 this.xxx = xxx;
3.在构造函数中,隐士的 return 这个添加了属性的 this 对象,return this;

8:this 的指向:

 

1.预编译过程中 this –> window
2.全局作用域 this –> window
3.call / apply 可以改变 this 的指向
4.obj.function(),function() 中的 this 指向 obj,谁调用 this,this指向谁

改变 this 的指向(3种):
1.bind:bind 函数用于将 当前函数 和 指定对象 绑定,返回一个 新的函数。当 新函数 被调用时,代码会在 指定对象 的 上下文 中执行。
2.call:Function.prototype 的一个方法。为了改变某个函数运行时的上下文。第一个参数为运行的上下文。
3.apply:Function.prototype 的一个方法。为了改变某个函数运行时的上下文。第一个参数为运行的上下文。

call / apply 区别:除了第一个参数,call 后面的 参数 是 一个一个 传的,而 apply 后面的 参数 是 放进 一个数组里 传的。

9:JS中改变this指向的方法(call和apply、bind)

 

call/apply

作用:改变this指向

区别:后面传的参数形式不同

function person(name, age) {
      this.name = name;
      this.age = age;
}
var obj = {};
person.call(obj, 'scarlett', 18);
console.log(obj.name); // scarlett 如果没call.obj默认为全局为window

如果用apply来传递实参的话,将是下面这种形式。

person.apply(obj, ['scarett', 17]);

1.call用作继承时

 

function Parent(age){
this.name=['mike','jack','smith'];
this.age=age;
}
function Child(age){
Parent.call(this,age);//把this指向Parent,同时还可以传递参数
}
var test=new Child(21);
console.log(test.age);//21
console.log(test.name);// ["mike", "jack", "smith"]
test.name.push('bill');
console.log(test.name);//mike,jack,smith,bill

2:call 的核心功能,它允许你在一个对象上调用该对象没有定义的方法,并且这个方法可以访问该对象中的属性

 

var a = {
    name :11,
    say:function(){
        console.log(this.name);
    }
}
function b(){
    console.log('1');
    console.log('2');
    this.say();
}
b.call(a);

输出1.2.11

 

10:继承

继承(6种方法):
1.原型链继承
function Father () {}
function Son () {}
Son.prototype = new Father()

缺点:会继承过多没有用的属性,造成大量的浪费。

2.构造函数继承 call / apply
function Father () {}
function Son () {Father.call(this)}

缺点:种方式不属于继承,也访问不了原型的原型。每次构造一个对象都要走两个构造函数,效率很低。

3.共享原型
function Father () {}
function Son () {}
Son.prototype = Father.prototype;

缺点:没办法改变子类的原型,一改就两个一起改了。

4.object.create()
function Father () {}
var son = Object.create(new Father);

5.圣杯模式

var inherit = (function (){
    var F = function(){};
    return function (C,P){
        F.prototype = P.prototype;
        C.prototype = new F();
        C.prototype.constructor = C;
        C.prototype.uber = P;
    }
}());

F 函数的作用:沟通 P 和 C 的原型,这样我们改变 C 的原型的时候只会影响 F 而不会影响 P。

6.extands(es6 方法)
class Father {}
class Son extands Father {}

11:作用域 & 闭包(*)

作用域: 变量和函数生效的区域。

作用域链: 函数可以产生作用域,函数又可以嵌套,作用域之间就会产生嵌套关系,就产生了作用域链。

闭包: 闭包就是能够读取其他函数内部变量的函数

闭包定义: 在一个 函数内部 再定义 一个函数,并且这个 内部函数 与 外部函数 的 变量 有关联,通过返回这个 内部的函 来 访问 外部函数 里面的 变量。

立即执行函数: 解闭包的一个重要方法,通过另一个新闭包来消除上一个闭包的影响
(function () {} ())
(function () {})()

function myAge () {
    var age = 0;
    return {
        add: function () {
            age++;
        },
        say: function () {
            console.log(age);
        }
    }
}
var my = myAge();
my.say(); // 0
my.add();
my.add();
my.say(); // 2

闭包

 function f1(){
     var a = 13;
     b = function(){
         a++;
     }
     function f2(){
     alert(a)
     }
     return f2;
 }
 var result = f1();
result();
b();
result(); 先弹出13,后弹出14

 

 在这段代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次的值是13,第二次的值是14。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。

为什么会这样呢?原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。

这段代码中另一个值得注意的地方,就是"b = function(){ a++;}"这一行,首先在b前面没有使用var关键字,因此b是一个全局变量,而不是局部变量。其次,b的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以b相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。

 

注意:

  function A(){
         var demo = 1;
         var B = function(){
             demo++
             console.log(demo)
         }
         return B;
     }
var C= A();
C();
C();
var D = A();
D();

 

使用闭包的注意点:

 

 1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。

2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。

 

立即执行函数:

 

立即执行函数是解闭包的一个重要方法。但是注意闭包是没有办法解除的,我们只能通过另一个新闭包来消除上一个闭包的影响。

定义:立即执行函数不需要被定义,直接执行,执行完毕之后直接释放。

经常被用作初始化。

立即执行函数的写法:

1.(function (a) {})(num);

2.(function (a) {} (num))

传递的参数是a,a的实参值是num,num是我们在外面定义的变量。

这两种写法功能完全一样,但是标准一点的写法是第二种。

• 这里有一个小知识点:函数声明不能被执行,但是函数表达式可以被执行

function test() {} ();// 错误
var test = function () {} ();// 这个是可以被执行的

第一种加一个括号变成立即执行函数就可以执行了。

 

(function () {} ());  // 正确
function returnB() {
      var arr = [];
      for (var i = 0; i < 10; i ++) {
            arr[i] = function () {
            console.log(i);
            }
      }
      return arr;
}
var save = returnB();
for (var i = 0; i < 10; i ++) {
      save[i]();
}

最后会输出10个10,并不是我们所想的0-9。

function returnB() {
      var arr = [];
      for (var i = 0; i < 10; i ++) {
            (function (n) {
                   arr[n] = function () {
                          console.log(n)
                   };
            }(i))
      }
return arr;
}
var save = returnB();
console.log(save);
for (var i = 0; i < save.length; i ++) {
      save[i]();
}

 

12:数组 Array

 

创建一个数组(2种方法):
1.数组字面量:var arr = [];

2.数组构造函数:var arr = new Array();

这种方式和字面量方式没有区别,但是要注意的是,如果我们在构造函数里面只写一个数字new Array(5);这个时候这个数字就不是第一个值是5的意思了,而是我们新创建的这个数组长度是5。

注意:数组可以溢出写,不可以溢出读

数组上的方法:改变原数组,不改变原数组

13:改变原数组

 

1.push 后进 / pop 后出

2.unshift 头进 / shift 头出

3.splice 截取 / 删除 / 插入(很强大的数组方法)
a.splice(b,c,1,2,3); //  在 a 中 从 b 位置截取到 c 位置,并替换成 1,2,3,如果c为0,则可以理解为插入

a.splice(b,c); //从a中删除b位置到c位置,c位置的数值不删除

例子:

 var colors = ["red","green","blue"];
  removed = colors.splice(1,1,"red","pup");
  console.log(colors)   //red,red,pup,blue
  console.log(removed)  //green

4.reverse 倒叙

5.sort 排序 可对字符串进行排序
若不传参数:按照字符的编码顺序进行排序。例:’a’在’b’前 , ‘11’在’12’前
若传参数:接受一个函数作为参数

a.sort(function (a,b) { 函数中可以传递两个参数
    return ; // return一个数值
             // 值为正a在b前
             // 值为负a在b后
    return a - b; // 从小到大排序
    return b - a; // 从大到小排序
    retutn Math.random() - 0.5 // 乱序
})
 function sort1(a,b)
{
return a-b;
}
var arr = [5,6,4,7,1,3]
 console.log(arr.sort(sort1));   正常排序

14 下列可以直接使用

 

Math 对象

属性
PI ∏ 的值, 约等于 3.1415
LN10 10 的自然对数的值,约等于 2.302
E Euler 的常量的值,约等于 2.718 Euler 的常量用作自然�%A-color: initial; border-image: initial; border-top-style: solid; border-top-color: rgb(221, 221, 221); background-color: rgb(247, 247, 247);">FirefoxGeckoOperaPresto->blink

Math.ceil(x)返回>=x的最小整数
Math.floor(x) 返回<=x的最大整数
Math.pow(x,y)返回x的y次方
Math.random()返回0-1之间的随机小数
Math.round(x)返回x的四舍五入的整数.

 

Data 方法的分组: 
setxxx 这些方法用于设置时间和日期值
getxxx  这些方法用于获取时间和日期值
Toxxx 这些方法用于从 Date 对象返回字符串值
parsexxx & UTCxx   这些方法用于解析字符串

用作 Date 方法的参数的整数: 
Seconds 和 minutes 0 至 59 
Hours 0 至 23 
Day 0 至 6(星期几) 
Date 1 至 31(月份中的天数) 
Months 0 至 11(一月至十二月)

Set 方法:
setDate 设置 Date 对象中月份中的天数,其值介于 1 至 31 之间。
setHours 设置 Date 对象中的小时数,其值介于 0 至 23 之间。
setMinutes 设置 Date 对象中的分钟数,其值介于 0 至 59 之间。 
setSeconds 设置 Date 对象中的秒数,其值介于 0 至 59 之间。 
setTime 设置 Date 对象中的时间值。 
setMonth 设置 Date 对象中的月份,其值介于 1 至 12 之间。

Get 方法:
getDate 返回 Date 对象中月份中的天数,其值介于 1 至 31 之间
getDay 返回 Date 对象中的星期几,其值介于 0 至 6 之间
getHours 返回 Date 对象中的小时数,其值介于 0 至 23 之间
getMinutes 返回 Date 对象中的分钟数,其值介于 0 至 59 之间
getSeconds 返回 Date 对象中的秒数,其值介于 0 至 59 之间
getMonth 返回 Date 对象中的月份,其值介于 0 至11 之间
getFullYear 返回 Date 对象中的年份,其值为四位数
getTime 返回自某一时刻(1970 年 1 月 1 日)以来的毫秒数

To 方法:
ToGMTString 使用格林尼治标准时间 (GMT) 数据格式将 Date 对象转换成字符串表示
ToLocaleString 使用当地时间格式将 Date 对象转换成字符串表示

Parse 方法和 UTC 方法 
Date.parse (date string ) 用日期字符串表示自 1970 年 1 月 1 日以来的毫秒数 
Date.UTC (year, month, day, hours, min., secs. ) Date 对象中自 1970 年 1 月 1 日以来的毫秒数4

 

15:JS中如何输出空格

1. 使用输出html标签&nbsp;来解决

 document.write("&nbsp;&nbsp;"+"1"+"&nbsp;&nbsp;&nbsp;&nbsp;"+"23");

输出  1    23     

2. 使用CSS样式来解决

 document.write("<span style='white-space:pre;'>"+"  1        2    3    "+"</span>");

输出1      2   3

 

 

16:浏览器内核

浏览器内核名字有很多,渲染引擎、排版引擎、解释引擎,英文Rendering Engine 
是用来渲染网页内容的,把你的网页代码转化为可见的页面 
在早期内核也是包含js引擎的,而现在js引擎越来独立了,可以把它单独提出来 
主流浏览器的内核及变化如下 

主流浏览器内核
IE -> Edgetrident->EdgeHTML
Chromewebkit->blink
Safariwebkit
FirefoxGecko
OperaPresto->blink

 

17:不改变原数组

 

1.concat 连接
var a = b.concat(c); // a 数组为 b 数组 连接 c 数组

2.slice 截取
var a = b.slice(c,d); // a 数组为 在 b 中,从 c 位置 截取到 d 位置

3.join 连接
var a = b.join(); // 若 不传 参数,则以 逗号 连接

4.(…) 扩展运算符 起到连接数组的作用
var a = [1,2,…b]; // a 数组为 1,2,连接 b 数组

es5 数组上的新方法(ECMAscript5增加的方法,IE8以下(包括IE8)浏览器不完全支持ES5

1.forEach() // 改变原数组,遍历数组中每一个元素

18:对象的枚举

 

or-in操作符

我们知道要枚举一个数组的所有元素,只要用一个for循环从头到尾遍历一遍就可以了。

但是对象并不能用for循环来遍历属性,所以这里我们就要使用for-in操作了。

var obj = {
      name: 'scarlett',
      age: 18,
      sex: 'female'
}
for(var prop in obj) {
      console.log(prop + ': ' + obj[prop]);
}

我们for-in循环会按照属性的顺序取出属性名然后 赋给prop,所以我们打印的prop都是属性名,obj[prop]则是相对应的属性的值

注意:这里我们不能写成obj.prop的形式,因为在系统底层会转化成obj[‘prop’]的形式,但是我们并没有prop这个属性,它应该是一个变量,所以会打印出来undefined,这里我们必须使用obj[prop]来调用

在es3和es5的非严格模式下,for-in循环都会把原型里面的属性一起打印出来,es5的严格模式不会

下面我们来介绍三种操作符:

 

 

 

1.hasOwnProperty

这个操作符的作用是查看当前这个属性是不是对象自身的属性,在原型链上的属性则会被过滤掉。如果是自身的,就返回true,否则返回false。

function Person () {
      this.name = 'scarlett'
}
Person.prototype = {
      age:18
}
var oPerson = new Person();
for(var prop in oPerson) {
      if (oPerson.hasOwnProperty(prop)) {
            console.log(oPerson[prop];
      }
}

,我们的for-in循环就会只打印自身的name属性而不会去打印原型上的age属性。

 

 

 

2.in操作符

这个操作符的作用是查看一个属性是不是在这个对象或者它的原型里面。

'name' in oPerson; // true
'age' in oPerson; // true
'sex' in oPerson; // false

3.instanceof操作符

这个操作符的作用是查看前面的对象是不是后面的构造函数构造出来的,和constructor挺像的。

oPerson instanceof Person; // true
oPerson instanceof Object; // true
{} instanceof Object; // true
{} instanceof Person; //false

也可以理解为:后面的构造器的原型是否在前面对象的原型链上

 


19:es5严格模式

 

es5的严格模式是一种全新的es5规范,在这个模式下,有一些es3的不标准的规则就不能使用了。

 

严格模式主要有两种用法:

1.全局严格模式

2.局部严格模式

全局模式就是我们在整个js代码的第一行写上字符串,而局部模式就是在函数里面的第一行写上字符串。

 

 

 

严格模式有什么作用?

1.当我们的代码进入严格模式之后,就不允许使用with函数,arguments.callee方法,func.caller属性

2.变量赋值之前必须声明

3.局部的this使用前必须被赋值,除了全局的this默认指向window,其他的默认都是undefiend。而且在非严格模式下,Person.call(null/undefined)之后,里面的this还是指向window,但是如果是严格模式的话,那么传递null,this就指向null,传递undefiend,this就指向undefiend

4.拒绝重复属性和参数。不过有一些浏览器的属性名可以重复。

with(慎用)

with () {} 的作用是改变作用域链,它可以把括号里面的执行期上下文或者作用域放在自己的作用域链的最顶端。

var obj = {a: 123};
function test () {
      var a = 111;
      var b = 222;
      with (obj) {
            console.log(a); // 123
            console.log(b); // 222
      }
}
test();

本来如果没有with的话,在test函数里面作用域链的最顶端应该是自身,然后在下面才是window的作用域,但是用with之后,我们把obj放在了自身上面,成为了最上面的作用域,这样我们打印a,就会优先调用obj里面的a属性,如果有就用,没有的话再向下找第二层test函数的作用域

 

 20:js定时器

 

 

在javascript中,与定时器有关的方法是:

setInterval、clearInterval

以及

setTimeout、clearTimeout

setInterval这个定时器的功能是每过一段时间,就把我们想要执行的函数放到js的执行队列中等待执行。因为执行队列不一定是空的,需要等执行队列中的所有任务都执行完之后才会执行我们的函数,因此这个函数执行的时间也会有细微的差别。

这个方法的语法是:

 

window.setInterval(function () {}, 1000);

第一个参数是我们要执行的函数,第二个参数是每过多长时间把函数放入执行队列。

这里要说明的是,第一个参数的那个函数,不能带有参数。其次,里面this默认指向window,因为前面提到过,谁调用方法,方法里面的this就指向谁,setInterval其实前面省略了window,因此里面的this默认一定指向window,不论这个setInterval是否是一个对象的方法。

setInterval其实很消耗内存,这个定时器一旦执行,就不会终止,因此需要我们的内核一直监听这个函数。

这个时候我们就需要一个方法来清除定时器了:clearInterval。

定时器其实会返回一个标记,我们可以通过定时器的这个标记来清除掉相对应的定时器。

     var i = 0;
var timer = setInterval(function () {
        i++,
      console.log(i);
      if(i == 10){
     clearInterval(timer);
        }
    }, 1000); //打印1-10十个数

特别强调:凡是写定时器,一定要清除定时器!

 

 

setTimeout是延迟执行的意思,语法和用法和setInterval一样,只是这个方法只是把函数延迟一段时间之后执行一次而已。

同时它也有clearTimeout,当我们不想让这个函数执行了,提前把它clear掉就可以了。

其实setInterval和setTimeout的第一个参数不一定非要是一个函数,它也可以是一串字符型的js代码。

setInterval("console.log(1);", 100);    

这段代码同样可以执行,定时器每100ms给我们打印一个1。

通过这个特性,我们不一定要把函数写在定时器里面,大部分时间我们都是在外部把函数定义好了,然后直接把函数名传进去就可以了

function test () {}
setInterval(test, 1000);   

21:事件处理模型与事件委托

事件处理模型——事件冒泡、事件捕获

什么叫冒泡?

结构上(非视觉上)嵌套关系的元素,会存在事件冒泡的功能,即同一事件,子元素冒泡向父元素,结构上的自底向上。(这里的底是结构上的底,视觉上是自顶向下)

大部分事件都有事件冒泡现象,并且所有的浏览器都有事件冒泡

并不是所有的事件都有冒泡,focus、blur、change、submit、reset、select等方法就没有事件冒泡现象。

<div class="wrapper">
           <div class="box">
               <div class="content">
               </div>
           </div>
        </div> 
var wrapper = document.getElementsByClassName('wrapper')[0],
	box = document.getElementsByClassName('box')[0],
	content = document.getElementsByClassName('content')[0];
    wrapper.onclick=function(){
       console.log('wrapper')
      };
      box.onclick=function(){
       console.log('box')
      };
      content.onclick=function(){
       console.log('content')
      }

显示效果:

依次点击黑黄粉 控制台打印如下:

这个现象就是我们常说的事件冒泡

 

 

事件捕获:

结构上(非视觉上)嵌套关系的元素,会存在事件捕获功能,即同一事件,自父元素捕获至子元素(事件源元素),结构上的自顶向下。

addEventListener最后一个参数就是是否开始事件捕获,当我们填true的时候,就代表开启了事件捕获。只要开启了事件捕获,就不会冒泡了,如果不捕获的话,就遵循事件冒泡。

因为addEventListener只有chrome有,因此事件捕获也只有chrome浏览器有

 

  var wrapper = document.getElementsByClassName('wrapper')[0],
	box = document.getElementsByClassName('box')[0],
	content = document.getElementsByClassName('content')[0];
wrapper.onclick = function () {
	console.log('wrappeBubbler');
}
box.onclick = function () {
	console.log('boxBubble');
}
content.onclick = function () {
	console.log('contentBubble');
}
wrapper.addEventListener('click', function (e) {
	console.log('wrapper');
}, true);
box.addEventListener('click', function (e) {
	console.log('box');
}, true);
content.addEventListener('click', function (e) {
	console.log('content');
}, true);

 

 

 

现在点击content之后,顺序是wrapper、box、content。

当事件冒泡和事件捕获同时存在的时候,事件冒泡和事件捕获的触发顺序则为:先捕获,再冒泡

 

结果是先捕获再冒泡。

但是当我们把捕获写到冒泡前面的时候,顺序好像发生了变化。

wrapper–>box–>contentBubble–>content–>boxBubble–>wrapperBubble

这里是因为点击content,并不属于冒泡,而是属于事件执行,我们先绑定的boxBubble,所以就先捕获,再事件执行,再冒泡,这与我们的结论没有冲突。

22:取消冒泡和阻止默认事件

 

有时候冒泡或者默认事件会对我们的功能造成影响,因此我们需要适时地取消冒泡和默认事件。

我们绑定事件的处理函数的时候,可以传递一个形参,代表我们的事件对象,一般是e或者event,系统会自动帮我们捕获事件源对象并且把事件源对象传入

取消冒泡的方法:

1.w3c标准方法:event.stopPropagation()

var wrapper = document.getElementsByClassName('wrapper')[0],
	box = document.getElementsByClassName('box')[0],
	content = document.getElementsByClassName('content')[0];
content.onclick = function (e) {
	console.log('content');
	e.stopPropagation();
}

现在我们点击content之后就没有冒泡了,但是点击box还是有冒泡的,因为我们没有取消box的冒泡。

 

ie9以及以下的版本不支持这个方法

 

2.event.cancelBubble = true

这个属性是IE的,不过一些高版本的浏览器也有这个属性,只要让这个属性的值等于true,同样也可以取消事件冒泡。

封装一个兼容性的取消事件冒泡的方法:

function stopBubble(event) {
	if(event.stopPropagation) {
		event.stopPropagation();
	}else {
		event.cancelBubble = true;
	}
}

默认事件

当我们在浏览器中点击右键,会弹出一个菜单,这就是一个默认事件contextmenu。还有a标签,即使我们不写跳转的页面,也会自动刷新页面,这也是一个默认事件。

移动端的默认事件更多。

默认事件有好的也有不好的,这就需要我们把不需要的默认事件阻止掉。

1.return false

 

我们只要在处理函数最后写上 return false就可以阻止默认事件了。

 

document.oncontextmenu = function () {
	console.log('menu');
	return false;
}

现在我们在页面上右键就不会出现菜单了。

不过要注意的是,这种写法只能用在句柄方式绑定的事件上

2.e.preventDefault()

documet.addEventListener('contextmenu', function (e) {
	console.log('menu');
	e.preventDefault();
},false);

这是w3c标准的阻止默认事件的方法,句柄也同样可以使用。

 

不过IE9以下不兼容

 

3.e.returnValue = false

这个是IE的方法,事件源对象上的属性returnValue代表是否有默认事件,直接返回false就可以阻止默认事件了。现在高版本的浏览器也有这个属性

document.attachEvent('oncontextmenu', function (e) {
	e.returnValue = false;
});

封装一个兼容性的阻止默认事件的方法了:

 

 
function cancelHandler(event) {
	if(event.preventDefault) {
		event.preventDefault();
	}else{
		event.returnValue = false;
	}
}
<a href="javascript: void(0); ">www.baidu.com</a>


小例子:阻止a标签不跳转

 

var a = document.links[0];
a.addEventListener('click', funciton (e) {
	e.cancelHandler(e);
},false);

同时我们还可以用a标签的第四个用处,协议限定符来阻止默认事件。

 

 
<a href="javascript: void(0); ">www.baidu.com</a>

不仅仅是0,只要填写一个代表false的值,就可以取消掉默认事件.

 

23:事件对象

 

在IE中,系统不会把事件对象传到方法中,因此我们的参数e或者event在IE中是不好用的,IE会把事件对象传递到window.event上,所以当我们使用事件对象的时候,就要写兼容性的写法

var event = e || window.event;

这样就可以正常地获取到事件对象了。

 

 

 

事件委托

事件源对象

我们现在有一个ul,下面有十万个li,当我们给父级的ul添加第一个点击事件之后,由于事件冒泡的存在,不论我们点击哪一个li都会调用父级的点击事件处理函数,这个时候触发父级ul的点击函数的那个li就被称之为事件源对象。

event.target 火狐的获取事件源对象

event.srcElement IE的获取事件源对象

chrome两种都有

因此我们在获取事件源对象的时候也需要写兼容性写法, 配合刚才的事件对象的兼容性写法就是这个样子的:

oUl.addEventListener('click', function (e) {
	var event = e || window.event;
	var tar = event.target || event.srcElement;
		console.log(tar);
	}, false);

我们利用事件源对象和事件冒泡来处理的方式就叫做事件委托。

 

oUl.addEventListener('click', function (e) {
	var event = e || window.event;
	var tar = event.target || event.srcElement;
	console.log(tar.innerHTML);
	}, false);

事件委托的优点

1.性能 不需要循环所有的子元素一个个绑定事件

2.灵活 当有新的子元素被加入的时候不需要重新绑定事件

24:异步加载JS

 

JSON:

我们传输数据就两种格式:xml和json

xml是以html为模板,自定义标签名作为数据名来传递数据,书写起来很麻烦,现在多用json,很少使用xml。

json是传输数据的另一种格式,它是以对象为模板(本质上就是对象,但是用途有所区别,对象是本地使用,json是用来传输数据)。

不过我们传输数据的时候并不能将一个json对象直接传过去,我们只能传过去json形式的字符串,这个时候我们就需要用到JSON上的两个方法了。

JSON.parse() —> string->json

JSON.stringify() —> json->string

通过JSON上的这两个方法,我们就可以进行数据传输了.

	var obj = {
		'name': 'scarlett',
		'age': 18
	};
	var string = JSON.stringify(obj);
	console.log(string); // '{"name":"scarlett","age":18}'
	var data = JSON.parse(string);
	console.log(data); // Object {name: "scarlett", age: 18}

 我们的页面有三个树:DOMTree、CSSTree、renderTree。(实际上多于三个),renderTree上有两个规则:repaint和reflow,重绘和重排。

repaint是元素自身的位置和宽高不变,只改变颜色的之类的属性而不会导致后面的元素位置的变化的时候,renderTree发生的动作。

reflow是元素自身的位置或者宽高改变了从而导致的整个页面的大范围移动的时候,renderTree发生的动作。

所以我们在DOM操作的时候,要尽量避免重排

JS异步加载部分

我们前面知道script标签的特点是会阻塞后面的DOM解析,只有当script标签下载完成并且全部执行完之后,浏览器才会继续解析DOM。

这样就导致了js加载有这样的缺点:加载工具方法本身没有必要阻塞文档,js加载会影响整个页面效率,一旦网速不好,那么整个网站将等待js加载而不进行后续渲染。

DOMTree就是解析DOM结构的,当我们在解析DOM的时候突然碰到一个script标签,那么这个script标签就会阻断DOMTree和CSSTree,然而我们有一些js的工具库并不操作DOM,这个时候我们就需要让这些工具库进行异步加载或者按需加载了。

以前有一种写法是将script标签写在body的最后面,等在DOM全部解析完成之后才加载js。

现在有了html5标准之后,就有了另一套异步加载js的方法了。

js异步加载的三种方案:

1.defer异步加载

我们在script标签的行间写一个defer=“defer”或者直接写defer,就可以让这个script变成异步加载了。但是这种异步只是针对下载方面,只有等DOMTree全部解析完成(不包括下载完里面的资源)后才会异步执行。而且这个方法只有IE和一些高版本的firefox和chrome可以用

<script type="text/javascript" defer="defer">
	console.log('hello');
</script>

不过这一种方式可以在script标签里面写代码

注意:IE6和IE7的异步加载最多只能有2个,当多余两个的时候必须等前两个加载完才会加载第三个。

所有defer的js代码都保证按照顺序执行

2.async异步加载

async是asynchronous的缩写,它是html5的标准,下载完成之后就会立即异步执行,这种方法除了IE6-IE8其他的浏览器都好用

<script type="text/javascript" async="async"></script>

不过这种方式不能把代码写在script标签里面,只能引用。(虽然标准是这么写的,但是现在随着内核升级,async里面也可以写代码了,在没有src的情况下)。

而且async的js代码不能保证是顺序执行的。

• 这两个属性不能一起使用

 

图中蓝色部分属于js加载过程,红色部分表示js执行过程,绿色代表DOM解析过程。

 

兼容性写法:

1.直接写两个script标签,一个是defer,一个是async。

但是这种方法有缺陷:IE高版本会加载两遍引起冲突,有些浏览器两个都没有会一个都加载不出来。

所以我们就需要用第二种方法了。

2.通过动态添加script标签。

“Script-inserted script elements now have async default to true, which can be set to false to make the scripts execute in insertion order.”

w3c的标准规定:动态添加的script标签是异步的

通过这个特性,我们这里就可以封装一个兼容性的异步加载js的函数了。

function asyncLoaded(url, callback){
	var script = document.createElement('script');
	script.type = 'text/javascript';
	if(script.readyState){//IE和高版本的chrome、firefox
		script.onreadystatechange = function(){
			if(script.readyState = 'loaded' || script.readyState == 'complete'){
				script.onreadystatechange = null;
				callback && callback();
			}
		}
	}else{
	script.onload = function(e){//Safari Chrome Opera Firefox
		callback && callback();
		}
	}
	script.src = url;
	document.body.appendChild(script);
}

 

需要注意的是:readtState的if-else一定要写在script.src=url和appendChild之前,因为电脑速度可能会很快,刚走到src=url部分就已经加载完毕了,readyState已经变成了loaded,后面就不会触发onreadystatechange事件了。

这里src部分的下载是异步的,不会阻塞后面的代码的执行,也就是说可以一边把script插入到DOM中一边下载资源。

还有一点,如果我们的回调函数是写在需要加载进来的文件里面方法的话,我们就需要把这个方法放到匿名函数里面,这样在语法解析的时候才不会因为函数未声明而报错。

• 异步加载js不允许使用document.write这个方法。它会清除文档流,一旦执行就会把全部的标签都清除掉,包括自身的script标签。

JS加载时间线(performace timing line)

1、创建Document对象,开始解析web页面。解析HTML元素和他们的文本内容后添加Element对象和Text节点到文档中。这个阶段document.readyState = ‘loading’。
2、遇到link外部css,创建线程加载,并继续解析文档。
3、遇到script外部js,并且没有设置async、defer,浏览器加载,并阻塞,等待js加载完成并执行该脚本,然后继续解析文档。
4、遇到script外部js,并且设置有async、defer,浏览器创建线程加载,并继续解析文档。 对于async属性的脚本,脚本加载完成后立即执行。(异步禁止使用document.write())
5、遇到img等,先正常解析dom结构,然后浏览器异步加载src,并继续解析文档。
6、当文档解析完成,document.readyState = ‘interactive’。
7、文档解析完成后,所有设置有defer的脚本会按照顺序执行。(注意与async的不同,
但同样禁止使用document.write());
8、document对象触发DOMContentLoaded事件,这也标志着程序执行从同步脚本执行阶段,
转化为事件驱动阶段。
9、当所有async的脚本加载完成并执行后、img等加载完成后,document.readyState = ‘complete’,window
对象触发load事件。
10、从此,以异步响应方式处理用户输入、网络事件等。

 

 

 

25:BOM

 

BOM的全称叫做Browser OjbectModel 浏览器对象模型,它定义了操作浏览器的接口。

BOM对象包括:Window、History、Navigator、Screen和Location。但是由于浏览器厂商的不同,BOM对象的兼容性极低,一般情况下,我们只用其中的一部分功能。

Navigator

navigator.userAgent是我们经常使用的属性,它可以返回客户端的代理值。用这个我们就可以判断当前用户使用的是pc端还是移动端,是什么浏览器。

Screen

screen.availHeight/availWidth 可以查看除了window任务栏之外的屏幕的高度和宽度。

screen.height/width 返回显示器的屏幕的高度和宽度。不过这个兼容的较少。

screen.deviceXDPI/deviceYDPI返回显示屏幕每英寸的水平/垂直点数,即分辨率。DPI越高,分辨率越高。

History

history.length 可以返回当前窗口查看过的网页记录的数量,即浏览历史的长度。

history.back() 可以让浏览器进入到下一个历史页面。

history.forward()  可以让浏览器返回到上一个历史页面。

history.go() 这个方法有一个参数,当参数是正数的时候,前进到下一个历史页面,当是负数的时候,回退到上一个历史页面。即前两个的综合方法。

Location

location.host 可以返回当前的主机名和当前的URL端口号。比如百度就返回www.baidu.com

location.search 设置或返回从问好开始的URL(查询部分)

location.hash 可以跳转到相应的id的元素的位置,和a标签的锚点功能差不多。可以配合css3的:target一起使用改变元素的样式。

location.reload() 方法可以重新加载当前页面

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值