1.浏览器

IE——trident
Chrome——webkit / blink
Firefox——gecko
Opera——presto
Safari——webkit

2.数据类型

a. 原始值:
存放在栈中(stack: first in last out)(不可改变的原始值)
Number Boolean String undefined null
栈相当于内存,内存中的数据不会更改,只会依次往下一个内存单元格里储存,直到内存用 完,才开始进行新一轮覆盖。
原始值存放时把值放在栈中,然后将栈的单元格编号改为变量名。
b. 引用值:
大致存放在堆中(heap)
array Object function date RegExp…
引用值存放时把值放在堆中,把堆的单元格编号放在栈中,形成指向性的指针,然后将栈的单元格编号改为变量名。

3.下列方法后面不用加分号(;)

function test () {}
for () {}
if () {}

4.逻辑运算符(&& || !)

a. &&:先看第一个表达式转换为布尔值的结果,如果为false,则返回第一个表达式的值,如果结果为真,那么它会看第二个表达式转换为布尔值的结果;如果只有两个表达式的话,只要看到第二个表达式,就可以返回该表达式的值了。如果有多个表达式以此类推。
undefined、null、NaN、""、0、false转换为布尔值的结果都为false

var a = 1 && 2;
console.log(a); // 2

1 && document.write(‘Hello World’); //短路语句

b. || :依次看表达式转换为布尔值的结果,遇真则返回该表达式的值。
c. !

typeof()

typeof()返回六种数据类型:
number 、 string 、boolean 、undefined 、 object 、 function

console.log(typeof(null))        //object,  null为原始值,但是历史遗留问题,当初浏览器用它为对象占位;var num = 123;
console.log(typeof num)     //number,  typeof num 这种格式不常用;
console.log(typeof(typeof(num)))        //string,  typeof()返回值的类型为string

JS运行三部曲

语法分析 ——> 预编译 ——>解释执行

预编译 四部曲

函数体系内的预编译:
1.创建AO对象(Activation Object, 作用域,执行期上下文);
2.找形参和变量声明,将变量和形参作为AO属性名,值为undefined;
3.将实参值和形参统一;
4.在函数体里找函数声明,值赋予函数体。

全局预编译
1.创建GO对象(Global Object, GO === window);
2.找形参和变量声明,将变量和形参作为GO属性名,值为undefined;
3.将实参值和形参统一;
4.找函数声明,值赋予函数体。

未经声明就赋值的变量归全局所有(归GO所有)

只有表达式才能被执行符号()执行,及立即执行函数

正确:

    function abc (){var num = 123;console.log(num);}abc();   //正确

错误:

    function abc (){var num = 123;console.log(num);}()   //错误, 此为函数声明

表达式定义广泛,除了非表达式,都是表达式!

abc      //表达式
123     //表达式
abc = funciton () {}        //表达式
i++     //表达式
function test () {}     //非表达式,函数声明;

计算符号可以使函数声明变为表达式,如下:

+function test () {}     //表达式
-function test  () {}       //表达式
!function test () {}     //表达式

由此可得到立即执行函数(立即执行函数的不规范格式):

// ()让函数声明变为表达式
(function test () {})       //表达式
//即为表达式,便可执行
(function test () {console.log('a')
})()        //正确,可执行

立即执行函数的标准格式和不标准格式:

(function () {} ())      //标准
(function () {})()      //不标准

标准格式中的最外层()为数学运算符号,优先级高;{}后面的()为执行符号,优先级低,故和非标准格式一样;

如下表达式被执行后,就会失去对原来函数的索引:

var demo = function () {console.log('a');
}()
console.log(demo);          //undefinedif(function foo (){}){console.log(typeof foo);       //报错console.log(typeof foo);        //undefined
}

但凡可以把function变成表示式的,都会让function失去对自身的索引。

闭包

当内部函数被保存到外部时,将会生成闭包。闭包会导致原有作用域链不释放,造成内存泄露。
闭包:

function test(){var arr = [];for(var i = 0; i < 10; i++){arr[i] = function(){console.log(i);}}return arr;
}var myArr = test();
for(var j = 0; j < 10; j++){myArr[j]();
}
//执行结果为console了10个10。因为console.log(i)这个函数并没有被执行,而是保存到了arr中,形成闭包,这个函数的作用域链(AO)没有释放,保存出来的i已经被i++为10

利用立即执行函数和闭包原理解决闭包问题

function test(){var arr = [];for(var i = 0; i < 10; i++){(function (j) {arr[j] = function(){console.log(j);}}(i))}return arr;
}var myArr = test();
for(var j = 0; j < 10; j++){myArr[j]()
}       //执行结果为console了0,1,2,3,4,5,6,7,8,9

闭包案例:

打印被点击的li的索引
<ul><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li>
</ul>
错误:
function demo () {var Lis = document.getElementsByTagName('li');for(var i = 0; i < Lis.length; i++){Lis[i].onclick = function(){console.log(i);}}
}
demo();正确(利用闭包):
function demo () {var Lis = document.getElementsByTagName('li');for(var i = 0; i < Lis.length; i++){(function(j){Lis[j].onclick = function(){console.log(j);}}(i));}
}
demo();正确(利用ES6的let):
function demo (){var Lis = document.getElementsByTagName("li");for(let i = 0; i < Lis.length; i++){Lis[i].onclick = function(){console.log(i);}}
}
demo();

对象

对象有属性,有方法

var andy = {name: '属性';health: 100;run: function (){     //方法console.log('I like running !');this.health++;};drink: function(){      //方法console.log('I like drinking !');this.health--;}
}
andy.run        //表示函数的引用;
andy.run()      //表示函数的执行;

1. 对象属性的增、删、改、查

 增加属性: andy.wife = 'Nancy';删除属性: delete andy.drink;修改属性: andy.wife = 'Kelly';查询属性: andy.name;当一个变量没有经过定义使用的话会报错;当一个对象的属性没有定义便访问的话会打印undefined

2. 对象的创建方法

  1. var obj = {} plainObject 对象字面量/对象直接量
  2. 构造函数
    1. 系统自带的构造函数 new Object()
      使用方法如:var obj = new Object();

    2. 自定义

      function Student ( name, age, sex){this.name = name;this.age = age;this.sex = sex;this.grade = 2018;
      }
      var student = new Student( 'Andy', 18, 'male');
      

    构造函数必须遵循大驼峰式命名方法

构造函数内部原理

构造函数三段式,前提是前面必须加上new

  1. 在函数体最前面隐式的加上var this = {};

  2. 执行this.xxx = xxx;

  3. 隐式的返回this

    function Student ( name, age, sex){this.name = name;this.age = age;this.sex = sex;this.grade = 2018;
    }
    var student = new Student( 'Andy', 18, 'male');一旦执行了new,就有如下步骤:
    function Student ( name, age, sex){//1. 在函数体最前面隐式的加上this = {};// var this = {};this.name = name;this.age = age;this.sex = sex;this.grade = 2018;//2. 执行this.xxx = xxx;//var this = {//   this.name = name;// this.age = age;//   this.sex = sex;//   this.grade = 2018;//}//3. 隐式的返回this//return this;
    }
    

包装类

属性和方法只有对象能有,对象包括对象自己、数组、函数;
原始值是没有属性和方法的。

数字有原始值型数字和对象型数字,字符串、布尔值也是;
对象型的可以有属性和方法。
undefined 、null 不可以有属性和方法。

当一个变量没有经过定义使用的话会报错;
当一个对象的属性没有定义便访问的话会打印undefined

var num = 123;   //原始值型
console.log(num);
var num = new Number(123);      //对象型数字,可以有属性、对象,也可以参与运算,但是运算结束后又变成原始值型数字
console.log(num);
num = num * 2;
console.log(num)

字符串有个系统自带的属性length。(其实也是系统隐式的进行了包装类)

var str = 'abc;
console.log(str.length);    //3

原始值没有属性和方法,但是执行如下代码并不会报错:

var str = 'abc';
str.abc = 'a';              //此时刷新页面,执行不会报错
console.log(str.abc);       //undefined(不报错)var  num = 3;
num.len = 5;                //此时刷新页面,执行不会报错
console.log(num.len);       //undefined(不报错)

以上代码执行时有一过程叫——包装类,如下:

//包装类
var  num = 3;
num.len = 5;
//原始值不能有属性,当调用属性时会发生一个隐式的过程
//系统会新建一个数字对象,然后执行上边的属性操作,执行完后会自行消除掉
//new Number(3).len = 5;  delete
console.log(num.len);
//当你再次调用这个属性时,系统会再次新建一个数字对象,然后执行属性的访问操作,执行完后再消除掉
//new Number(3).len
//由于构造函数创建的对象是各自独立的,所以这个对象并没有len属性,所以打印结果为undefined

这个隐式的过程叫做包装类。
常见骗术

var arr = [1, 2, 3, 4, 5];
arr.length = 2;
console.log(arr);       //[1, 2]//基于以上结果,如下类似的是否成立var str = 'abcd';
str.length = 2;
console.log(str);
//结果为'abcd',因为字符串为原始值,执行过程经历了包装类。

案例:

var str = 'abc';
str += 1;
var test = typeof(str);
if(test.length = 6){test.sign = 'typeof的返回值可能为String';
}
console.log(test.sign);

考题:

var x = 1, y = z = 0;
function add(n){return n = n + 1;
}
y = add(x);
function add(n){return n = n + 3;
}
z = add(x);
console.log(x, y, z)        //1, 4, 4   执行预编译,函数提升,不要看表面书写顺序
//下面代码中console.log结果是[1, 2, 3, 4, 5]的选项是()
//A
function foo(x){console.log(arguments);return x
}
foo(1, 2, 3, 4, 5);//B
function foo(x){console.log(arguments);return x;
}(1, 2, 3, 4, 5)//C
(function foo(x){console.log(arguments);return x;
})(1, 2, 3, 4, 5)//D
function foo(){bar.apply(null, arguments)}
function bar(x){console.log(arguments)}
foo(1, 2, 3, 4, 5)//A C D
//以下表达式的执行结果是()
parseInt(3, 8)
parseInt(3, 2)
parseInt(3, 0)
//A
3 3 3
//B
3 3 NaN
//C
3 NaN Nan
//D
other// C和D都可以, parseInt(3, 0)在不同浏览器中执行结果不一样,0为基底的不同执行结果
//封装一个方法,求字符串的字节长度,提示:unicode > 225时字节长度为2,unicode  <= 225时字节长度为1//case 1
function getCodeLen (str) {var len = 0;for(var i = 0; i < str.lenth; i++){if(str.charCodeAt(i) > 225){len += 2;}else{len++;}}return len;
}//case 2
function getCodeLen2 (str) {var len = str.length;for(var i = 0; i < str.length; i++){if(str.charCodeAt(i) > 225){len++;}}return len;
}

undefined、null没有包装类。

原型

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

利用原型特点和概念,可以提取共有属性。

对象如何查看原型——>隐式属性__proto__

对象如何查看对象的构造函数——>constructor

原型自己可以增删改,后代不可以对原型进行增删改。后代利用xxx.xxx直接修改算特例,其实相当于原型本身自己改,因为这样直接修改了引用值的堆单元格里的值。(后面有例)

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

function Student () {
}
//Student.prototype —— 原型
//Student.prototype = {} ——是祖先
Student.prototype.school = '北京大学';
var zhangSan = new Student();
var liSi = new Student();
console.log(zhangSan.school ,  liSi.school);

原型里有而构造函数本身没有的属性,访问时取原型上的;原型里有而构造函数本身也有的属性,访问时取函数本身的。

Student.prototype.grade = 1;
function Student (){this.grade = 2;
}
var wangWu = new Student();
console.log(wangWu.grade);

利用原型特点和概念,可以提取共有属性。

Car.prototype.brand = 'BMW';
Car.prototype.lenght = 5200;
Car.prototype.height = 1500;
Car.prototype.width = 1850;
function Car (color, owner){this.color = color;this.owner = owner;
}
var car1 = new Car('white', 'Andy');
var car2 = new Car('blue', 'Bale');
console.log(car1.brand, car2.brand);

通过赋值可以修改当前构造函数本身的属性,但是无法修改原型的属性。如修改上例中的属性须使用:Car.prototype.brand = ‘Benz’
在构造函数中对原型的增删改查,只有查可以实现。
原型的简化写法:

Car.prototype = {brand: 'BMW';length: 5200;height: 1500;width: 1850;
}
function Car (color, owner){this.color = color;this.owner = owner;
}
var car1 = new Car();

对象如何查看对象的构造函数——>constructor

function Car (color, owner){this.color = color;this. owner = owenr;
}
var car1 = new Car();
//控制台输入
//car1.constructor
//得到Car构造函数

对象的构造函数可以修改:

function Car(color, owner){this.color = color;this.owner = owner;
}
function Student(){this.name = Jack;
}
var car1 = new Car();
//修改方法1
Car.prototype = {constructor: Student;
}
//修改方法2
car1.constructor = Student;//控制台输入car1.constructor等到Student函数

对象如何查看原型——>隐式属性_proto_
对于前面的构造函数的三段式:
构造函数三段式,前提是前面必须加上new,1 在函数体最前面隐式的加上var this = {};2. 执行this.xxx = xxx;3. 隐式的返回this
其中第一步中加的var this = {}, 并不是空对象,而是含有__proto__的对象,其中__proto__相当于桥梁、连接指向

Car.prototype.brand = 'BMW';
function Car () {//var this = {//   __proto__: Car.prototype;//}
}
var car1 = new Car();
//所以car1.brand可以访问

这个桥梁/连接指向是可以修改的:

var obj = {brand: 'Benz'
};
Car.prototype.brand = 'BMW';
function Car () {};
var car1 = new Car();
console.log(car1.brand);        //结果为BMWcar1.__proto__ = obj;
console.log(car1.brand);        //结果为Benz//但是并没有改变构造函数的原型
var car2 = new Car();
console.log(car2.brand);        //结果为BMW

手动修改原型时的问题:
直接用xxx.prototype.xxx = xxx修改

Car.prototype.brand = 'BMW';
function Car () {}
var car1 = new Car();
Car.protoype.brand = 'Benz';
console.log(car1.brand);        //结果为Benz

用xxx.prototype = { xxx: xxx}修改

Car.prototype.brand = 'BMW';
function Car () {}
var car2 = new Car();
Car.prototype = {brand: 'Benz'
}
console.log(car2.brand);        //结果为BMW//修改失败原因如下:
var obj = {name: 'a'};      //引用值
var obj1 = obj;
obj = {name: 'b'};
console.log(obj1);      //obj1的值并不会改变
//xxx.prototype = {xxx: xxx}方法亦是如此
var obj = {name: 'a'};      //引用值
var __proto__ = obj;
obj = {name: 'b'};
console.log(__proto__);     //__proto__的值并不会改变
//执行步骤:
//Car.prototype.brand = 'BMW';
//function Car () {
//  var this = {__proto__: Car.prototype}
//此时__proto__指向的和Car.prototype是一个空间
//}
//var car2 = new Car();
//Car.prototype = {
//  brand: 'Benz'
//此时Car.prototype更换了空间,而__proto__指向的空间并没有更换,还是原来的空间,所以值仍为原来的BMW
//}

xxx.prototype.xxx = xxx与xxx.prototype = { xxx: xxx}修改的方法和过程是不一样的,前者直接修改本对象指向的堆中的单元格里的值,后者是在堆中新的单元格中存放一个新的值,然后改变本对象的指针(在栈中存放的堆的单元格编号),使其指向新的单元格。详见原始值与引用值。

函数执行顺序问题,更换xxx.prototype = { xxx: xxx}位置,即可修改成功:

Car.prototype.brand = 'BMW';
function Car () {}
Car.prototype = {brand: 'Benz'
}
var car2 = new Car();
console.log(car2.brand);        //Benz
//函数Car()在new的时候才执行,而此时Benz已经修改了prototype的指针,所以__proto__指向了新的Benz

原型自己可以增删改,后代不可以对原型进行增删改。后代利用xxx.xxx直接修改算特例,其实相当于原型本身自己改,因为这样直接修改了引用值的堆单元格里的值。

原型链

原型还可以有原型,原型连成串,就是原型链。
原型链的最顶端是Object.prototype。最终原型
原型链上属性的增删改查
绝大多数对象的最终都会继承自Object.prototype
Object.create(原型)

Person.prototype = {name: 'a',sayName: function () {console.log(this.name);}
}
function Person () {this.name = 'b';
}
var person = new Person();
//控制台输入:
//person.name               结果为b
//person.sayName()      结果为b//sayName()里面的this指向:谁调用这个方法,this就指向谁

sayName()里面的this指向:谁调用这个方法,this就指向谁

Person.prototype = {height: 100
}
function Person () {this.eat = function () {this.height ++;}
}
var person = new Person();
//控制台输入:
//person.eat()      //结果为undefined,因为函数没有设置return,默认undefined
//person
//person.__proto__

对象自变量也有原型;
var obj = {} 也有原型,var的过程系统隐式的进行了var obj = new Object();

Object.create(原型) 也可以创建对象,这个放入的原型必须是对象或者null

//var obj = Object.creat(原型)
var obj = {name: 'sunny', age: 123};
var obj1 = Object.create(obj);
console.log(obj1.name, obj1.age);       //sunny 123
//obj为obj1的原型
Person.prototype.name = 'sunny';
function Person () {}
var person = Object.create(Person.prototype);
console.log(person.name);       //sunny

特例: Object.create(null)可以创建没有原型的对象。

var obj = Object.create(null);
//控制台输入:
//obj       点开后没有__proto__//没有原型的对象无法添加原型
控制台输入:
//obj.__proto__ = {name: 'Andy'}
//obj.name      结果为undefined

undefined、null没有原型,没有包装类。

原型链方法重写:原型上有某一方法,自身又写了一个同一名字,不同功能的方法。
有人为重写,有系统自带重写

Object.prototype.toString
Number.prototype.toString       //系统自带重写,重写后的方法
Object.prototype.toString.call(123)     //原型链顶端中的方法
Arry.prototype.toString         //系统自带重写,重写后的方法
Boolean.prototype.toString      //系统自带重写,重写后的方法
String.prototype.toString       //系统自带重写,重写后的方法

document.write()打印的是toString()方法结果,而不是()中的结果

var obj;
document.write(obj);        //undefindedvar obj = Object.create(null);
document.write(obj);        //报错。因为Object.create(null)创建的对象没有原型,所以没有toString()方法,故报错var obj = Object.create(null);
obj.toString = function(){return "HELLO WORLD";
}
document.write(obj);        //HELLO WORLD

javascript 可以计算的数值范围,小数点前16位,小数点后16位

call / apply

call/apply可以改变this的指向
call 需要把实参按照形参的个数传进去;
apply 需要传一个arguments(数组)

function test () {}
test();     //内部是这样执行的test.call()var obj = {};
console.log(obj);       //undefinded
function Person (name, age) {this.name = name;this.age = age;
}
Person.call(obj, 'Andy', 18);       //第一个参数obj为改变的this指向,第二个参数为正常传参的第一个参数,后面的参数以此类推
console.log(obj);

实例应用:

//员工A需要一个构造函数Person
funtion Person (name, age, sex) {this.name = name;this.age = age;this.sex = sex;
}
//员工B需要一个构造函数Student
function Student (name, age, sex, tel, grade){this.name = name;this.age = age;this.sex = sex;this.tel = tel;this.grade = grade;
}
//很明显A和B的函数功能有相同或相似之处,这时可以用call()来精简代码
//员工B的函数可以这样写
function Student (name, age, sex, tel, grade){Person.call(this, name, age, sex);this.tel = tel;this.grade = grade;
}
//call()/apply()基本只有一个功能就是改变this的指向,借以实现借用别人的方法实现自己的功能

应用实例:

//身体=头+颈+躯干+四肢
//Body()=Tou() + Jing() + Qugan() + Sizhi()
function Tou (eye, nose, mouth){this.eye = eye;this.nose = nose;this.mouth = mouth;
}
function Jing (length, coarseness){this.length = length;this.coarseness = coarseness;
}
function Qugan (chest, abdomen){this.chest = chest;this.abdomen = abdomen;
}
function Sizhi (arm, leg){this.arm = arm;this.leg = leg;
}
function Body (eye, nose, mouth, length, coarseness, chest, abdomen, arm, leg){Tou.call(this, eye, nose, mouth);Jing.call(this, length, coarseness);Qugan.call(this, chest, abdomen);Sizhi.call(this, arm, leg);
}
var body1 = new Body('bule', 'big', 'big', 'short', 'thin', 'plump', 'fat', 'strong', 'short');
console.log(body1);

继承发展史:

1、传统形式——>原型链
过多的继承了没用的属性
2、借用构造函数
不能继承借用构造函数的原型
每次构造函数都要多走一个函数
3、共享原型
不能随便改动自己的原型
4、圣杯模式

圣杯模式:

Father.prototype.lastName = 'Ji';
function Father () {}
function F () {}
F.prototype = Father.prototype;
Son.prototype = new F();
function Son () {}
var son = new Son();
console.log(son.lastName);  //Ji//提取一个公式,封装为inherit函数
function inherit (Target, Origin){function F (){};F.prototype = Origin.prototype;Target.prototype = new F();
}//测试一下封装的函数
Father.prototype.sex = 'male';
function Father () {}
function Son () {}
inherit(Son, Father);
var son = new Son();
var father = new Father();
console.log(son.sex, father.sex);   //male male
Son.prototype.like = 'read';        //Son单独设定原型的属性,不会影响原型链上的上代函数
console.log(son.like, father.like); //read undefined// 扩展
console.log(son.custructor)     //并非Son()而是Father()
//son.__proto__ ——> new F()对象身上没有custructor,所以再往上找, new F().__proto__ ——> Father.prototype

命名空间

管理变量,防止污染全局,适用于模块化开发。
现在几乎不用,现在主要用闭包来解决。

对象的枚举(遍历)

for in

  1. hasOwnPropety
  2. in
  3. instanceof

for in循环专门针对对象使用

var obj = {name: 'Andy,age: 18,sex: 'famale',height: 178,weight: 75
}
//obj.name等价于obj['name']
console.log(obj.name)           //Andy
console.log(obj['name']         //Andy//遍历对象用for in
for(var key in obj){console.log(obj.key)        //undefined 因为等价于obj['key'],而obj没有为key属性console.log(obj[key])        //正确,因为key本身就是变量,不用加引号console.log(obj[key], typeof(obj[key]);
}

for循环可以遍历出原型上手动加的属性,但是遍历不到原型链最顶端的系统的属性

var obj = {name: 'Andy',age: 18,sex: 'male',height: 180,weight: 75,__proto__: {like: 'football',love: 'girl',__proto__: {class: 'two',grade: 'three',__proto__: Object.prototype}}
}
Object.prototype.country = 'China';
for(var key in obj){console.log(obj[key]);      //football,girl,two,three,China均可遍历出来
}

hasOwnProperty() 自己的属性,不包括原型上的属性,返回值为布尔值

var obj = {name: 'Andy',age: 18,sex: 'male',height: 180,weight: 75,__proto__: {like: 'football',love: 'girl',__proto__: {class: 'two',grade: 'three',__proto__: Object.prototype}}
}
Object.prototype.country = 'China';
for(var key in obj){if(obj.hasOwnProperty(key)){console.log(obj[key]);      //football,girl,two,three,China均不可遍历出来}
}

通常for in循环里面配套if循环使用

in 只能判断对象有没有该属性,不能区分是自己的属性还是原型上的属性,返回值为布尔值
对象的属性名为字符串

var obj = {name: 'Andy',age: 18,height: 180,weight: 75
}
//控制台输入
height in obj           //报错,因为没有height变量
'height' in obj         //true

A instanceof B 判断A对象是不是B构造函数构造出来的A对象
看A对象的原型链上有没有B的原型
应用:

//判断aaa是数组还是对象,
var aaa;
//方法1
aaa.constructor     //数组为function Arry() { [native code] }, 对象为function Object() { [native code] }
//方法2
aaa instanceof Arry         //如果是数组返回值为true,如果是对象返回值为false
aaa instanceof Object       //如果是对象返回值为true,如果是数组返回值为false
//方法3Object.prototype.toString = function () {//谁调用了它,this就指向谁//1.识别this//2.返回相应的结果
}
aaa.toString()
//控制台输入
Object.prototype.toString.call(aaa)
//如果是对象,结果为"[object Object]"
//如果是数组,结果为"[object Array]"

,逗号操作符,看一眼逗号前面的,看一眼逗号后面的,然后返回逗号后面的返回。要实现逗号操作符必须用()包起来。

var a = (1, 2);      //a的值为2
var a = 1,2         //报错

tips:引用值相比较的是指向的地址,所以:

{} == {}         //falsevar obj = {};
var obj1 = obj;
obj == obj1         //true

this

1.函数预编译过程中this——>window
2.全局作用域里this——>window
3.call/apply 可以改变函数运行是this的指向
4.obj.function(); function()里this指向obj
函数在全局环境中被直接调用,严格模式下函数内this指向undefined,非严格模式下函数内this指向window。

var foo = 123;
function print(){this.foo = 234;console.log(foo);
}
print()     //234
//全局执行时,
//function print (){
//  隐式的进行了this的声明
//  var this = window;
//}
new print()     //123
//new print()执行时,
//function print(){
// 隐式的进行了this的声明
//  var this = Object.create(print.prototype);
//}

new function()会改变function中this的指向

arguments

  1. arguments.callee 指向函数自身引用
  2. function.caller 在哪个环境被调用就指向哪个环境

实例:

var bar = {a: '002'};
function print(){bar.a = 'a';Object.prototype.b = 'b';return function inner (){console.log(bar.a);console.log(bar.b);}
}
print()();          //a, b

克隆

  1. 浅层克隆
  2. 深层克隆

浅层克隆:

var obj = {name: 'Andy',age: 18,sex: 'female',card: ['visa', 'unionpay']
}
var obj1 = {};
function clone (original, target){var target = target || {};      //容错处理,防止调用时不传第二个参数for(var prop in original){target[prop] = original[prop];}return target;
}
obj1 = clone(obj, obj1);
console.log(obj1);
//此时的为浅层克隆,如源对象中有引用值,则源对象更改时,目标对象也会更改
obj.card.push('master');
console.log(obj1);

深度克隆
步骤:

  1. 遍历对象 for in 循环
  2. 判断对象的每个属性值是不是原始值 typeof() object
  3. 如果是引用值,判断是数组还是对象 instanceof / toString / constructor
  4. 建立相对应的数组或对象
  5. 递归 循环以上步骤
var obj = {name: 'Adny',age: 24,card: ['visa', 'unionpay'],wife: {name: 'Linda',age: 23,son: {name: 'Jacky',age: 2}}
};
var obj1 = {};
console.log(obj1);
function deepClone (origin, target){var target = target || {},toStr = Object.prototype.toString,arrStr = '[object Array]';for(var prop in origin){if(origin.hasOwnProperty(prop)){if(origin[prop] !== 'null' && typeof(origin[prop]) == 'object'){if(toStr.call(origin[prop]) == arrStr){target[prop] = [];}else{target[prop] = {};}deepClone(origin[prop], target[prop]);}else{target[prop] = origin[prop];}}}return target;
}
obj1 = deepClone (obj);
console.log(obj1);
obj.card.push('master');
console.log(obj);
console.log(obj1);

三目运算

条件判断 ? 是 : 否 并且会返回值
比if多一个return,有返回值

var num = 1 > 0 ? 2+2 : 1+1;       //4
var num1 = 1 < 0 ? 2+2 : 1+1;       //2

数组

数组是特殊的对象
数组的两种产生方法:

  1. var arr = [] 字面量
  2. var arr = new Array()
    两个方法的来源都是Array.prototype
    var arr = new Array(10) 如果只传一个参数,那么它会当做数组的长度,且不能传非整数

数组的读和写:
arr[num] //不可以溢出读(undefined,不会报错)
arr[num] //可以溢出写

数组常用方法
改变原数组:
push,pop,shift,unshift,sort,reverse,splice
不改变原数组:
concat,join ——> split,toString,slice

push()

//自写push()方法
Array.prototype.push = function(){for(var i = 0; i < arguments.length; i++){this[this.length] = arguments[i];}return this.length;
}
//测试
var arr = [1,2];
arr.push(3,4,5);
console.log(arr);

pop() 把数组最后一位剪切出来,原数组少了一位,且方法return了被剪切掉的数,不接收传参

var arr = [1, 2, 3];
var num = arr.pop();
console.log(num)            //3
console.log(arr)            //[1, 2]

unshift()在数组前面增加,跟push()方向相反,可以传参
shift()在数组前面剪除,可以传参
reverse()倒序原数组并返回

splice()可以传三个参
arr.splice(从第几位开始,截取多少长度,在切口处添加新的数据)
从第一个参数位开始,截取第二个参数长度的数据(改变原数组,并以数组形式返回截取的数值),在切口处添加第三个参数的内容。
splice()内部是如下处理第一个参数的,所以如果第一个参数传负数,从结果上看就是从倒数开始
funtion splice (pos){
pos += pos > 0 ? 0 : this.length; //判断是否传入负数
//pos >= 0 && pos < this.length 是才有意义
}

sort()给数组排序,改变原数组。根据内容的字符串值排序(ASCII码),并非数字排序

var arr = [1,5,2,10,9,8];
arr.sort()      //[1, 10, 2, 5, 8, 9]

所以,sort()提供了接口可以在里面放函数,但是函数有以下规则:

  1. 必须写两个形参
  2. 看返回值:
    1. 当返回值为负数时,那么前面的数放在前面
    2. 为正数,那么后面的数在前
    3. 为0,不动
var arr = [1, 2, 10, 15, 35, 3, 4 ,50,15];
arr.sort(function(a,b){//升序if(a > b){return 1;}else{return -1;}//降序if(a < b){return 1;}else{return -1;}//优化return a - b       //升序return b - a        //降序
})

Math.random()返回0到1之间的开区间数
开区间,不包括两端
闭区间,包括两端
左开右闭,左闭右开,以此类推

给一个有序数组乱序

var arr = [1,2,3,4,5,6,7];
arr.sort(function(a,b){return Math.random() - 0.5;
});

给一个值为对象的数组排序:

var zhang = {name: 'zhang',age: 18,sex: 'male'
};
var wang = {name: 'wang',age: 20,sex: 'female'
};
var zhao = {name: 'zhao',age: 23,sex: 'female'
};
var arr = [zhang, wang, zhao];
//按照年龄排序
arr.sort(function(a, b){return a.age - b.age    //年龄升序return b.age - a.age  //年龄降序
});var arr2 = ['sdfasf', 'sa', 'fasfasfsf', 'asgfarhvgfiearhgv', 'rvf'];
//按照字符串长度排序
arr2.sort(function(a, b){return a.length - b.length;
});var arr3 = ['1属于', '2张', '32王', 'sfs李', 'sfksfh', 'ds', 'dsas']
//按照字节长度排序
//先封装一个求字节长度的函数returnBytes
function returnBytes (str){var num = str.length;for(var i = 0; i < str.length; i++){if(str.CharCodeAt(i) > 225){num++;}}return num;
};
arr3.sort(function(a, b){return returnBytes(a) - returnBytes(b)
});

concat() 连接两个数组,并返回拼接好的新数组,原数组不改变
toString() 把数组变成字符串,并返回,原数组不改变
slice() 截取数组片段,并返回,原数组不改变
可以传0、1、2个参数
传2个参数时:slice(从该位开始截取,截取到该位)
传1个参数时:slice(从该位开始截取且一直截取到最后)
不传参数时:slice()整个截取

join() 传参必须为字符串,将数组内容以传入的字符串连接为一个字符串,并返回,原数组不改变
split() 传参必须为字符串,将字符串内容以传入的字符串拆分为一个数组,并返回,原字符串不改变
join()跟split()实现字符串和数组互相转换

var arr = [1, 2, 3, 4, 5];
var str = arr.join('-');
console.log(arr);       //[1, 2, 3, 4, 5]
console.log(str);       //1-2-3-4-5
var arr1 = str.split('-');
var arr2 = str.split('3');
console.log(str);       //1-2-3-4-5
console.log(arr1);      //["1", "2", "3", "4", "5"]
console.log(arr2);      //["1-2-", "-4-5"]

类数组

是对象,但可以当数组一样用
类数组必要条件:属性要为索引(数字)属性,必须要有length属性,最好加上push

//类数组构建
var obj = {'0': 'a','1': 'b','2': 'c','length': 3,'push': Array.prototype.push
}
//调用push方法
obj.push('d');
console.log(obj)                    //{0: "a", 1: "b", 2: "c", 3: "d", length: 4, push: ƒ}
//实现了对象不可以实现的操作
console.log(typeof(obj));           //object
//再给obj加上splice属性,则obj形状完全同数组一样
var obj = {'0': 'a','1': 'b','2': 'c','length': 3,'push': Array.prototype.push,'splice': Array.prototype.splice
}
console.log(obj);                   //["a", "b", "c", push: ƒ, splice: ƒ]
console.log(typeof(obj));           //object

例题:

var obj = {'2': 'a','3': 'b','length': 2,'push': Array.prototype.push
}
obj.push('c');
obj.push('d');
//求obj最终的结果//push方法原理
// Array.prototype.push = function(){
//     for(var i = 0; i < arguments.length; i++){
//         this[this.length] = arguments[i];
//     }
//     return this.length;
// }//答案:{2: "c", 3: "d", length: 4, push: ƒ}

DOM方法能够生成的数组全是类数组

typeof()完善版:

function myTypeof (target){var template = {'[object Array]': 'array','[object Object]': 'object','[object Number]': 'number-object',         //包装类,数字型对象'[object Boolean]': 'boolean-object',     //包装类,布尔型对象'[object String]': 'string-object'            //包装类,字符串型对象}if(target == null){return 'null';}else if(typeof(target) == 'object'){var str = Object.prototype.toString.call(target);return template[str];}else{return typeof(target);}
}

数组去重

//数组去重
Array.prototype.unique = function(){var temp = {},arr = [],len = this.length;for(var i = 0; i < len; i++){if(!temp[this[i]]){temp[this[i]] = 'abc';arr.push(this[i]);}}return arr;
}
var arr = [1,1,2,2,3,3,5,5,4,4];
var arr1 = arr.unique(arr);

通过var生成的window上的属性,叫做不可配置性属性,不可配置性属性delete不掉

复习例题

var name = 'window';
var obj = {name: 'obj',say: function(){console.log(this.name);}
}obj.say();              //obj
obj.say.call();         //window   ??call()不传参时默认全局调用,相当于window下调用

undefined和null不能跟数进行比较,因为他俩代表的是自己类型的原始值

try{}catch(e){}

try里面发生错误,不会执行错误后的try里面的代码,但try外面的后面的代码会继续执行,try里发生错误,但不会抛出错误。catch会收集try里面的错误,传递到形参e里面。

try{console.log('a');console.log(b);         //发生错误,但是不会抛出错误console.log('c');     //不会执行
}catch(e){console.log(e.name + ": " + e.message);       //打印错误
}
console.log('d');           //会执行

error.name的六种值对应的信息:

  1. EvalError: eval()的使用与定义不一致;
  2. RangeError: 数值越界;
  3. ReferenceError: 非法或不能识别的引用数值;
  4. SyntaxError: 发生语法解析错误;
  5. TypeError: 操作数类型错误;
  6. UrlError: Url处理函数使用不当

当前浏览器执行js的规则:
基于es3.0 + es5.0的新增方法
es3.0和es5.0产生冲突的部分:
es5.0的严格模式下冲突部分用es5.0,否则用es3.0

es5.0严格模式

“use strict” es5.0严格模式的启动

  1. 不再兼容es3的一些不规则语法,使用全新的es5规范
  2. 两种用法:
    1. 全局严格模式
    2. 局部函数内严格模式(推荐)
  3. 就是一行字符串,不会对不兼容严格模式的浏览器产生影响。
  4. 不支持with, arguments.callee, func.caller,变量赋值前必须声明,局部this必须被赋值(Person.call(null/undefined)赋值什么就是什么),拒绝重复属性和参数。

with(){}改变作用域链

var obj = {name: 'obj'
};
var name = 'window';
function test (){var name = 'scope';with(obj){console.log(name);}
}
test();     //obj//在命名空间中的应用
var org = {dp1: {jc: {name: 'abc',age: 18},deng: {name: 'efg',age: 23}},dp2: {}
}
with(org.dp1.jc){console.log(name, age);
};
with(org.dp1.deng){console.log(name, age);
}

DOM操作

  1. DOM ——> Document Object Model
  2. DOM定义了表示和修改文档所需的方法。DOM对象即为宿主对象,由浏览器厂商定义,用来操作html和xml功能的一类对象集合。也有人称DOM是对HTML和XML的标准编程接口。

1.对节点的增删改查


查看元素节点(get是实时的,query不是实时的)

  1. document代表整个文档
  2. document.getElementById() //元素id; 在ie8以下的浏览器,不区分id大小写,而且也返回匹配name属性的元素
  3. getElementsByTabName() //标签名
  4. getElementsByName() //需注意,只有部分标签name可生效(表单、表单元素、img、iframe)
  5. getElementsByClassName() //类名; ie8和ie8以下的ie版本中没有,可以多个class一起
  6. querySelector() //css选择器;在ie7和ie7以下的版本中没有;基本废弃不用,因为不是实时的,只是当时创建的副本
  7. querySelectorAll() //css选择器;在ie7和ie7以下的版本中没有;基本废弃不用,因为不是实时的,只是当时创建的副本

遍历节点树(元素节点、属性节点、文本节点、注释节点、document、DocumentFragment)

  1. parentNode //父节点(最顶端的parentNode为#document)
  2. childNodes //子节点们
  3. firstChild //第一个子节点
  4. lastChild //最后一个子节点
  5. nextSibling //后一个兄弟节点
  6. previousSibling //前一个兄弟节点

基于元素节点树的遍历(元素节点)

  1. parentElement //返回当前元素的父元素节点(IE不兼容)
  2. children //只返回当前元素的元素子节点
  3. node.childElementCount === node.children.length //当前元素节点的子元素个数(IE不兼容)
  4. firstElementChild //返回的是第一个元素节点(IE不兼容)
  5. lastElementChild //返回的是最后一个元素节点(IE不兼容)
  6. nextElementSibling / previousElementSibling //返回后一个/前一个兄弟元素节点(IE不兼容)

节点的四个属性:nodeName、nodeValue、nodeType、attributes
nodeName: 元素的标签名,以大写形式表示,只读
nodeValue: Text节点或Comment节点的文本内容,可读写
nodeType: 该节点的类型,只读(元素节点1、属性节点2、文本节点3、注释节点8、document9、DocumentFragment11)
attributes: Element节点的属性集合

例:不用children,获取子元素节点

<div class="box"><span></span><p></p><div></div><b></b><h2></h2>
</div><script>
var div = document.getElementsByClassName('box')[0];
console.log(div.childNodes);
function returnElementChild (node){var temp = {length: 0,push: Array.prototype.push,splice: Array.prototype.splice},child = node.childNodes,len = child.length;for(var i = 0; i < len; i++){if(child[i].nodeType == 1){temp.push(child[i]);}}return temp;
}
console.log(returnElementChild(div));</script>

例: 属性节点

<div id="box" class="demo"></div>
<script>
var div = document.getElementsByClassName('demo')[0];
console.log(div.attributes);
console.log(div.attributes[0]);
console.log(div.attributes[0].nodeType)
</script>

节点的一个方法:Node.hasChildNodes()判读节点是否有子节点,返回布尔值

<div class="demo"></div>
<script>
var div = document.getElementsByClassName('demo')[0];
console.log(div.hasChildNodes());       //true
</script><div class="demo"></div>
<script>
var div = document.getElementsByClassName('demo')[0];
console.log(div.hasChildNodes());       //false
</script>

DOM结构树

DOM基本操作:

  1. getElementById方法定义在Document.prototype上,即Element节点上不能使用
  2. getElementsByName方法定义在HTMLDocument上,即非html中的document不能使用(xml document,Element)
  3. getElementsByTagName方法定义在Document.prototype 和 Element.prototype上
  4. HTMLDocument.prototype定义了一些常用的属性,body、head分别指代HTML文档中的< body >、< head >标签
  5. Document.prototype上documentElement属性,指代文档的根元素,在HTML文档中,它总是指代< html >元素
  6. getElementsByClassName、quetySelectorAll、quetySelector在Document.prototype, Element.prototype类中均有定义

例:封装函数,返回元素e的第n个兄弟元素节点,n为正,返回后面的兄弟元素节点,n为负,返回前面的,n为0,返回自己

<div>abc<!-- this is comment --><span></span><p></p><strong></strong><i></i><address></address>
</div>
<script>
function returnSibling (e, n){while(e && n){if(n > 0){if(e.nextElementSibling){e = e.nextElementSibling;}else{   //兼容ie9for(e = e.nextSibling; e && e.nodeType != 1; e.nextSibling);}n--;}else{if(e.previousElementSibling){e = e.previousElementSibling;}else{  //兼容ie9for(e = e.previousSibling; e && e.nodeType != 1; e.previousSibling);}n++;}}return e;
}
</script>

  1. document.createElement()
  2. document.createTextNode()
  3. document.createComment()
  4. document.createDocumentFragment()

  1. PARENTNODE.appendChild() //剪切操作
  2. PARENTNODE.insertBefore(a, b) //insert a before b

  1. parent.removeChild() //父级删除子元素,剪切出来
  2. child.remove() //自己删除自己,真销毁

替换

  1. parent.replaceChild(new, origin)

Element节点的一些属性

  1. innerHTML //可读可写,写的话是覆盖
  2. innerText(火狐不兼容) / textContent(老版本ie不好使)

Element节点的一些方法

  1. ele.setAttribute()
  2. ele.getAttribute()

例:封装insertAfter函数,功能类似insertBefore

<div><i></i><b></b><span></span>
</div>
<script>
Element.prototype.insertAfter = function(targetNode, afterNode){var beforeNode = afterNode.nextElementSibling;if(beforeNode == null){this.appendChild(targetNode);}else{this.insertBefore(targetNode, beforeNode);}
}var div = document.getElementsByTagName('div')[0];
var b = document.getElementsByTagName('b')[0];
var span = document.getElementsByTagName('span')[0];
var p = document.createElement('p');
// 控制台输入:
div.insertAfter(p, b);
div.insertAfter(p, span);
</script>

日期对象Date()

例:秒表

<style>
input{border: 1px solid rgba(0, 0, 0, 0.8);text-align: right;font-size: 20px;font-weight: bold;
}
</style>
<div>minutes: <input type="text" value="0">seconds: <input type="text" value="0">
</div>
<script>
var minutesNode = document.getElementsByTagName('input')[0];
var secondsNode = document.getElementsByTagName('input')[1];
var minutes = 0,seconds = 0;
setInterval(function(){seconds++;if(seconds == 100){seconds = 0;minutes++;}minutesNode.value = minutes;secondsNode.value = seconds;
}, 10)

查看滚动条的滚动距离

  1. window.pageXOffset/pageYOffset //ie8及ie8以下不兼容
  2. document.body/documentElement.scrollLeft/scrollTop //兼容性比较混乱,用时取两个方法的值相加,因为不可能存在两个方法同时有值。
  3. 封装兼容性方法,求滚动轮滚动距离getScrollOffset()
    例:兼容性方法
function getScrollOffset(){if(window.pageXOffset){return {x: window.pageXOffset,y: window.pageYOffset}}else{return {x: document.body.scrollLeft + document.documentElement.scrollLeft,y: document.dody.scrollTop + document.documentElement.scrollTop,}}
}

查看视口尺寸

  1. window.innerWidth/innerHeight //ie8 及 ie8 以下版本不兼容
  2. document.documentElement.clientWidth/clientHeight //标准模式下,任意浏览器都兼容。
  3. document.body.clientWidth/clientHeight //适用于怪异模式下的浏览器
  4. 封装兼容性方法,返回浏览器视口尺寸 getViewportOffset()

HTML中有为标准模式,没有为怪异模式
查看浏览器模式方法: document.compatMode,值为‘CSS1Compat’是标准模式,值为‘BackCompat’为怪异模式。
例: 兼容性方法封装

function getViewportOffset (){if(window.innerWidth){return {w: window.innerWidth,h: window.innerHeight}}else{if(document.compatMode == 'BackCompat'){return {w: document.body.clientWidth,h: document.body.clientHeight}}else{return {w: document.documentElement.clientWidth,h: document.documentElement.clientHeight}}}
}

查看元素的几何尺寸
domEle.getBoundingClientRect()
兼容性很好
该方法返回一个对象,对象里面有left, right, top, bottom等属性。left 和 top 代表该元素左上角的X和Y坐标,right 和 bottom 代表该元素右下角的X和Y坐标。
height 和 width属性老版本ie并未实现。
返回的结果不是“实时的”(静态写照)。

查看元素的尺寸
dom.offsetWidth, dom.offsetHeight //不包含margin
查看元素的位置

  1. dom.offsetLeft, dom.offsetTop
    对于无定位父级的元素,返回相对文档的坐标。对于有定位父级的元素,返回相对于最近的有定位的父级的坐标。
  2. dom.offsetParent
    返回最近的有定位的父级。如无,返回body。body.offsetParent返回
    eg: 求任意元素相对于文档的坐标 getElementPosition

让滚动条滚动
window上有三个方法: scroll(), scrollTo(), scrollBy()
三个方法功能类似,用法都是将X、Y坐标传入,即实现将滚动轮滚动到当期位置。
区别: scrollBy()会在之前的数据基础上做累加。
eg:利用scrollBy()快速阅读的功能。

<div class="demo">1早上,我从医院出来,进了万寿寺,踏着满地枯黄的松针,走进了配殿。我真想把鞋脱下来,用赤脚亲近这些松针。古老的榆树,矮小的冬青丛,都让我感到似曾相识;令人遗憾的是,这里有股可疑的气味,与茅厕相似,让人不想多闻。配殿里有个隔出来的小房间,房间里有张桌子,桌子上堆着写在旧稿纸上的手稿。这些东西带着熟悉的气息迎面而来——过去的我带着重重叠叠的身影,飘扬在空中。用不着别人告诉,我就知道,这是我的房间、我的桌子、我的手稿。这是因为,除了穿在身上的灰色衣服,这世界上总该有些属于我的东西——除了有些东西,还要有地方吃饭,有地方睡觉,这些在目前都不紧要。目前最要紧的是,有个容身的地方。坐在桌子后面,我心里安定多了。我面前还放了一个故事。除了开始阅读,我别无选择了。“晚唐时,薛嵩在湘西当节度使。前往驻地时,带去了他的铁枪。”故事就这样开始了。这个故事用黑墨水写在我面前的稿纸上,笔迹坚挺有力。这种纸是稻草做的,呈棕黄色,稍稍一折就会断裂,散发着轻微的霉味。我面前的桌子上有不少这样的纸,卷成一捆捆的,用橡皮筋扎住。随手打开一卷,恰恰是故事的开始。走进万寿寺之前,我没想到会有这么多故事。可以写几个字来对照一下,然后就可认定是不是我写了这些故事。但我觉得没有必要。在医院里醒来时,我左手的食指和中指上,都有黑色的墨迹。这说明我一直用黑墨水来写字。在我桌子上,有一个笔筒,里面放满了蘸水钢笔,笔尖朝上,像一丛龙舌兰的样子;笔筒边上放着一瓶中华牌绘图墨水。坐在这个桌子面前,我想到:假如我不是这个故事的做者,也不会有别人了;虽然我一点不记得这个故事。这些稿子放在这里,就如医院窗台上的《暗店街》。假如我不来认领,就永无人来认领。这世界上之所以会有无主的东西,就是因为有人失去了记忆。莫迪阿诺在《暗店街》里写道:“我的过去一片朦胧……”这本书就放在窗台上,是本小册子,黑黄两色的封面,纸很糙,清晨微红色的阳光正照在它身上。病房里住了很多病人,不知它是谁的。我观察了许久,觉得它像是件无主之物,把它拿到手里来看;但心中惕惕,随时准备把它还回去。过了很久也没人来要,我就把它据为己有。过了一会儿,我才骤然领悟到:这本书原来是我的。这世界上原来还有属于我的东西——说起来平淡无奇,但我确实没想到。病房里弥漫着水果味、米饭味、汗臭味,还有煮熟的芹菜味。在这个拥挤、闭塞、气味很坏的地方,我迎来了黎明。我的过去一片朦胧……病房里有一面很大的玻璃窗。每天早上,阳光穿过不平整的窗玻璃,在对面墙上留下火红的水平条纹;躺在这样的光线里,有如漂浮在熔岩之中。本来,我躺在这张红彤彤的床上,看那本书,感到心满意足。事情忽然急转而下,大夫找我去,说道,你可以出院了。医院缺少床位,多少病人该住院却进不来——听他的意思,好像我该为此负责似的。我想要告诉他,我是出于无奈(别人用汽车撞了我的头)才住到这里的,但他不像要听我说话的样子,所以只好就这样了。此后,我来到大街上,推着一辆崭新的自行车,不知该到哪里去。一种巨大的恐慌,就如一团灰雾,笼罩着我——这团雾像个巨大的灰毛老鼠,骑在我头上。早晨城里也有一层雾,空气很坏。我自己也带着医院里的馊味。我总觉得空气应该是清新的,弥漫着苦涩的花香——如此看来,《暗店街》还在我脑中做祟……莫迪阿诺的主人公失去了记忆。毫无疑问,我现在就是失去了记忆。和他不同的是,我有张工做证,上面有工做单位的地址。循着这个线索,我来到了“西郊万寿寺”的门前。门洞上方有“敕建万寿寺”的字样,而我又不是和尚……这座寺院已经彻底破旧了。房檐下的檩条百孔千疮,成了雨燕筑巢的地方,燕子屎把房前屋后都变成了白色的地带,只在门前留下了黑色的通道。这个地带对人来说是个禁区。不管谁走到里面,所有的燕巢边上都会出现燕子的屁股,然后他就在缤纷的燕粪里,变成一个面粉工人。燕子粪的样子和挤出的儿童牙膏类似。院子里有几棵白皮松,还有几棵老得不成样子的柏树。这一切似曾相识……我总觉得上班的地点不该这样的老旧。顺便说一句,工做证上并无家庭住址,假如有的话,我会回家去的,我对家更感兴趣……万寿寺门前的泥地里混杂着砖石,掘地三尺也未必能挖干净。我在寺门前逡巡了很久,心里忐忑不安,进退两难。直到有一个胖胖的女人经过。她从我身边走过时抛下了一句:进来呀,愣着干啥。这几天我总在愣着,没觉得有什么不对。但既然别人这么说,愣着显然是不对的。于是我就进去了。出院以前,我把《暗店街》放在厕所的抽水马桶边上。根据我的狭隘经验,人坐在这个地方才有最强的阅读欲望。现在我后悔了,想要回医院去取。但转念一想,又打消了这个主意。把一本读过的书留给别人,本是做了一件善事;但我很怀疑自己真有这么善良。本来我在医院里住得好好的,就是因为看了这本书,才遇到现在的灾难。我对别的丧失记忆的人有种强烈的愿望,想让他们也倒点霉——丧失了记忆又不自知,那才是人生最快乐的时光……对于眼前这座灰蒙蒙的城市,我的看法是:我既可以生活在这里,也可以生活在别处;可以生活在眼前这座水泥城里,走在水泥的大道上,呼吸着尘雾;也可以生活在一座石头城市里,走在一条龟背似的石头大街上,呼吸着路边的紫丁香。在我眼前的,既可以是这层白内障似的、磨砂灯泡似的空气,也可以是黑色透明的、像鬼火一样流动着的空气。人可以迈开腿走路,也可以乘风而去。也许你觉得这样想是没有道理的,但你不曾失去过记忆——在我衣服口袋里,有一张工做证,棕色的塑料皮上烙着一层布纹,里面有个男人在黑白相片里往外看着。说实在的,我不知道他是谁。但是,既然出现在我口袋里,除我之外,大概也不会是别人了。也许,就是这张证件注定了我必须生活在此时此地。
</div>
<div class="start" style="border-radius: 25px; width: 50px; height: 50px; font-size: 20px; color: green; background-color: cyan; text-align: center; line-height: 50px; position: fixed; bottom: 100px; right: 50px; opacity: 0.7">Start</div>
<div class="stop" style="border-radius: 25px; width: 50px; height: 50px; font-size: 20px; color: red; background-color: orange; text-align: center; line-height: 50px; position: fixed; bottom: 30px; right: 50px; opacity: 0.7">Stop</div>
<script>
var start = document.getElementsByClassName('start')[0];
var stop = document.getElementsByClassName('stop')[0];
var timer = 0;
var key = true;
start.onclick = function(){if(key){timer = setInterval(function(){window.scrollBy(0, 10);},100);key = false;}
};
stop.onclick = function(){clearInterval(timer);key = true;
}
</script>

脚本化CSS

js不能直接改变css,但是可以间接改变css
读写元素CSS属性

  1. dom.style.prop
    可读写行间样式,没有兼容性问题,碰到float这样的保留字属性,前面应加css
    eg: float—>cssFloat
    复合属性必须拆解,组合单词变成小驼峰写法。
    写入的值必须是字符串格式。

查询计算样式

  1. window.getComputedStyle(ele, null)
    计算样式只读
    返回的计算样式的值都是绝对值,没有相对单位
    ie8及ie8以下不兼容

例: window.getComputedStyle(ele, null)

<div class="div1" style="width: 100px; height: 100px; background-color: green"></div>
<script>
var div = document.getElementsByClassName('div1')[0];
var mycss = window.getComputedStyle(div, null);
console.log(mycss);
</script>

window.getComputedStyle(ele, ull)可以获取伪元素的css, null传伪元素

<div class="div1" style="width: 100px; height: 100px; background-color: green;"></div>
<style>
.div1::after{content: '';width: 20px;height: 20px;background-color: yellow;display: inline-block;
}
</style>
<script>
var div = document.getElementsByClassName('div1')[0];
var mycss = window.getComputedStyle(div, 'after');
console.log(mycss);
</script>

window.getComputedStyle(ele, null)在 ie8及ie8以下不兼容,ie自己的方法:
ele.currentStyle //计算样式只读,返回的计算样式的值不是经过转换的绝对值,IE独有的属性
封装兼容性方法getStyle(elem, prop);

例: 封装兼容ie的获取计算样式的方法

function getStyle (elem, prop){if(window.getComputedStyle){return window.getComputedStyle(elem, null)[prop];}else{return elem.currentStyle[prop];}
}

例:操作伪元素思想

<style>
.green::after{content: 'green';color: #ffffff;font-size: 8px;text-align: center;line-height: 30px;width: 30px;height: 30px;background-color: green;display: inline-block;
}
.orange::after{content: 'orange';color: #ffffff;font-size: 8px;text-align: center;line-height: 30px;width: 30px;height: 30px;background-color: orange;display: inline-block;
}
</style>
<div id="box" class="green" style="width: 100px; height: 100px; background-color: blue"></div>
<script>
var box = document.getElementById('box');
var flag = true;
box.onclick = function(){if(flag){this.className = 'orange';flag = false;}else{this.className = 'green'flag = true;}
};
</script>

例: 滑动小方块

<div class="demo" style="width: 100px; height: 100px; background-color: red; position: absolute; left: 0; top: 0;"></div>
<script>
function getStyle(elem, prop){if(window.getComputedStyle){return window.getComputedStyle(elem, null)[prop];}else{return elem.currentStyle[prop];}
}
var div = document.getElementsByClassName('demo')[0];
var timer = setInterval(function(){div.style.left = parseInt(getStyle(div, 'left')) + 1 + 'px';if(parseInt(div.style.left) > 700){clearInterval(timer);}
}, 10);
</script>

事件

如何绑定事件处理函数

  1. ele.onXXX = function(){}
    兼容性很好,但是一个元素的同一个事件上只能绑定一个处理程序
    基本等同于写在HTML行间上
  2. ele.addEventListener(type, fn, false)
    ie9以下不兼容,可以为一个事件绑定多个处理程序,调用时type传参要加引号
  3. obj.attachEvent(‘on’ + type, fn)
    ie独有,一个事件同样可以绑定多个处理程序

例: ele.addEventListener(type, fn, false)

<div class="demo" style="width: 100px; height: 100px; background-color: orange"></div>
<script>
var div = document.getElementsByClassName('demo')[0];
div.addEventListener('click', function(){alert('a');
}, false);
div.addEventListener('click', function(){alert('b');
}, false);
//同一个事件绑定多次,只执行一次
function alertFn (){alert('a')
}
div.addEventListener('click', alertFn, false);
div.addEventListener('click', alertFn, false)
</script>

例: obj.attachEvent(‘on’ + type, fn)

<div class="demo" style="width: 100px; height: 100px; background-color: orange"></div>
<script>
var div = document.getElementsByClassName('demo')[0];
div.attachEvent('onclick', function(){alert('a');
});
div.attachEvent('onclick', function(){alert('b');
});//同一事件绑定多次,执行多次
function alertFn (){alert('abc');
}
div.attachEvent('onclick', alertFn);
div.attachEvent('onclick', alertFn);
div.attachEvent('onclick', alertFn);
</script>

例:ele.addEventListener(type, fn, false)

<!-- 使用原生js, addEventListener给每个li绑定一个click事件,事件要求输出li的顺序 -->
<style>li{width: 100px; height: 30px; background-color: orange; line-height: 30px; font-size: 16px; margin: 5px 0; text-align: center; color: #ffffff;}
</style>
<ul><li>abc</li><li>abc</li><li>abc</li><li>abc</li>
</ul><script>var lis = document.getElementsByTagName('li');for(var i = 0; i < lis.length; i++){(function (j) {lis[j].addEventListener('click', function(){console.log(j + 1)}, false);})(i)}
</script>
<!-- es6中直接用let即可,不用闭包 -->

事件处理程序的运行环境

  1. ele.onXXX = function(event){} //程序this指向是dom元素本身
  2. obj.addEventListener(type, fn, false) //程序this指向是dom元素本身,调用时type传参要加引号
  3. obj.attachEvent(‘on’ + type, fn) //程序this指向window,调用时type传参要加引号
  4. 封装兼容性的 addEvent(elem, type, handle)方法
    例:让obj.attachEvent的this指向obj
<div class="demo">abc</div>
<script>
var div = document.getElementsByClassName('demo')[0];
function test () {console.log(this);
}
div.attachEvent('onclick', function(){test.call(div);
});
</script>

例:封装兼容性的addEvent(elem, type, handle)

function addEvent (elem, type, handle){if(elem.addEventListener){elem.addEventListener(type, handle, false);}else if(elem.attachEvent){elem.attachEvent('on' + type, function(){handle.call(elem);})}else{elem[on + type] = handle;}
}
//调用时type传参要加引号

解除事件处理程序

  1. ele.onclick = false/’’/null
  2. ele.removeEventListener(type, fn, false) //调用时type传参要加引号
  3. ele.detachEvent(‘on’ + type, fn) //调用时type传参要加引号
  4. 若绑定匿名函数,则无法解除

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

事件冒泡
结构上(非视觉上)嵌套关系的元素,会存在事件冒泡的功能,即同一事件,子元素冒泡想父元素。(自底向上)
obj.addEventListener(type, fn, false)实现冒泡
事件捕获
结构上(非视觉上)嵌套关系的元素,会存在事件捕获功能,即同一事件,自父元素捕获至子元素(事件源元素)。(自顶向下)
obj.addEventListener(type, fn, true)实现捕获
IE没有捕获事件
触发顺序:先捕获,后冒泡
focus, blur, change, submit, reset, select等事件不冒泡。

一个对象的一个事件类型只能遵循一种事件处理模型,要么冒泡,要么捕获。
例:冒泡

<style>
.red{width: 300px; height: 300px; background-color: red;}
.orange{width: 200px; height: 200px; background-color: orange;}
.yellow{width: 100px; height: 100px; background-color: yellow;}
</style>
<div class="red"><div class="orange"><div class="yellow"></div></div>
</div>
<script>
var red = document.getElementsByClassName('red')[0];
var orange = document.getElementsByClassName('orange')[0];
var yellow = document.getElementsByClassName('yellow')[0];red.addEventListener('click', function(){console.log('red');
}, false);
orange.addEventListener('click', function(){console.log('orange');
}, false);
yellow.addEventListener('click', function(){console.log('yellow');
}, false);
//点击黄色方块,打印:yellow, orange, red
</script>

例: 捕获

<style>
.red{width: 300px; height: 300px; background-color: red;}
.orange{width: 200px; height: 200px; background-color: orange;}
.yellow{width: 100px; height: 100px; background-color: yellow;}
</style>
<div class="red"><div class="orange"><div class="yellow"></div></div>
</div>
<script>
var red = document.getElementsByClassName('red')[0];
var orange = document.getElementsByClassName('orange')[0];
var yellow = document.getElementsByClassName('yellow')[0];red.addEventListener('click', function(){console.log('red');
}, true);
orange.addEventListener('click', function(){console.log('orange');
}, true);
yellow.addEventListener('click', function(){console.log('yellow');
}, true);
//点击黄色方块,打印: red, orange, yellow; 点击时,红色和橙色方块依次执行事件捕获,黄色方块为点击事件本身的正常事件执行,而非捕获
</script>

例: 捕获和冒泡的触发顺序

<style>
.red{width: 300px; height: 300px; background-color: red;}
.orange{width: 200px; height: 200px; background-color: orange;}
.yellow{width: 100px; height: 100px; background-color: yellow;}
</style>
<div class="red"><div class="orange"><div class="yellow"></div></div>
</div>
<script>
var red = document.getElementsByClassName('red')[0];
var orange = document.getElementsByClassName('orange')[0];
var yellow = document.getElementsByClassName('yellow')[0];red.addEventListener('click', function(){console.log('redBubble');
}, false);
orange.addEventListener('click', function(){console.log('orangeBubble');
}, false);
yellow.addEventListener('click', function(){console.log('yellowBubble');
}, false);red.addEventListener('click', function(){console.log('red');
}, true);
orange.addEventListener('click', function(){console.log('orange');
}, true);
yellow.addEventListener('click', function(){console.log('yellow');
}, true);
//点击黄色方块,依次打印: red, orange,  yellowBubble, yellow, orangeBubble, redBubble; 当捕获至黄色方块时,黄色方块上的为正常事件执行,哪个事件先绑定哪个事件先执行,然后再继续执行冒泡事件
//点击橙色方块,依次打印: red, orangeBubble, orange, redBubble; 同上
</script>

取消冒泡和阻止默认事件

取消冒泡

  1. W3C标准 event.stopPropagation(); 但不支持IE9以下版本
  2. IE独有 event.cancelBubble = true;(谷歌已实现兼容)
  3. 封装兼容性的取消冒泡函数 stopBubble(event)
    obj.onlcick = function(e){} e中包含了事件发生时一系列的属性,记录事件发生时的一系列状态和信息。
    例: 封装兼容性函数取消冒泡事件stopBubble(event)
<div class="box" style="width: 100px; height: 100px; background-color: red"></div>
<script>
function stopBubble(event){if(event.stopPropagation){event.stopPropagation()}else{event.cancelBubble = true;}
}
var div = document.getElementsByClassName('box')[0];
document.onclick = function(){console.log('Are you crazy ?')
};
div.onclick = function(e){// e.stopPropagation();// e.cancelBubble = true;stopBubble(e);this.style.backgroundColor = 'green';
};
</script>

阻止默认事件
默认事件——表单提交、a标签跳转、右键菜单等

  1. return false; 以对象属性方式(句柄方式)注册的事件才生效
  2. event.preventDefault(); W3C标准,IE9以下不兼容
  3. event.returnValue = false; 兼容IE
  4. 封装兼容性的阻止默认事件的函数cancelHandler(event);
    例: 封装兼容性的阻止默认事件的函数cancelHandler(event)
<script>
function cancelHandler(event){if(event.preventDefault){event.preventDefault();}else{event.returnValue = false;}
};
//阻止默认事件——右键菜单
document.oncontextmenu = function (e){cancelHandler(e);console.log('a');
}
</script>

事件对象

event;window.event用于IE //var event = event || window.event
事件源对象:

  1. event.target; 火狐只有这个
  2. event.srcElement; IE只有这个
  3. 这俩Chrome都有
    兼容性写法 var target = event.target || event.srcElement

事件委托

利用事件冒泡, 和事件源对象进行处理
优点:

  1. 性能:不需要循环所有的元素一个个绑定事件
  2. 灵活:当有新的子元素时不需要重新绑定事件
    例:点击li打印li的内容
<ul><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li>
</ul>
<script>
//常规写法
// var lis = document.getElementsByTagName('li');
// for(var i = 0; i < lis.length; i++){
//     lis[i].onclick = function(){
//         console.log(this.innerText);
//     };
// }
//如果li足够多,for循环效率低;且不能覆盖后续动态加载的li
//利用冒泡和事件源对象
var ul = document.getElementsByTagName('ul')[0];
ul.onclick = function(e){var event = e || window.event;var target = event.target || event.srcElement;console.log(target.innerText);
}
</script>

事件分类

鼠标事件
click、mousedown、mousemove、mouseup、contextmenu、mouseover、 mouseout、mouseenter、mouseleave
用button来区分鼠标的按键:0/1/2
DOM3标准规定: click事件只能监听左键,只能通过mousedown和mouseup来判断鼠标键
如何解决mousedown和click的冲突

例:小方块拖拽

<div class="demo" style="width: 100px; height: 100px; background-color: red; position: absolute; left: 0; top: 0"></div>
<script>
var div = document.getElementsByClassName('demo')[0];
var disX,disY;
div.onmousedown = function(e){disX = e.pageX - parseInt(div.style.left);disY = e.pageY - parseInt(div.style.top);document.onmousemove = function(e){var event = e || window.event;div.style.left = event.pageX - disX + 'px';div.style.top = event.pageY - disY + 'px';};document.onmouseup = function(){document.onmousemove = null;}
};
//可封装为addEventListener形式的drag(elem)函数
</script>

例:mouseenter和mouseleave为新标准,对应的旧标准为mouseover和mouseout,同时绑定则新标准生效

<div class="demo" style="width: 100px; height: 100px; background-color: red;"></div>
<script>
var div = document.getElementsByClassName('demo')[0];
div.onmouseenter = function(){div.style.backgroundColor = 'orange';
};
div.onmouseleave = function(){div.style.backgroundColor = 'yellow';
};
div.onmouseover = function(){div.style.backgroundColor = 'green';
};
div.onmouseout = function(){div.style.backgroundColor = 'cyan';
};
</script>

只有mousedown和mouseup可以通过其事件event的button属性来区分左0/中1/右2键
例:

<script>
document.onmousedown = function(e){var event = e || window.event;// console.log(event);if(event.button == 0){console.log('left');}else if(event.button == 2){console.log('right');}else if(event.button == 1){console.log('middle');}};
</script>

例:区分click和mousedown

<script>
var firstTime;
var lastTime;
var flag = false;
document.onmousedown = function(){firstTime = new Date().getTime();
};
document.onmouseup = function(){lastTime = new Date().getTime();if(lastTime - firstTime < 300){flag = true;}
}
document.onclick = function(){if(flag){console.log('click');flag = false;}
}
</script>

例:10秒小游戏

<style>
h4{width: 290px; font-size: 50px; color: purple; text-align: center; margin: 10px 0;}
.start,.stop,.reset{width: 50px; height: 50px; border-radius: 50%; margin: 10px 20px; display: inline-block; line-height: 50px; font-size: 18px; text-align: center; color: white; cursor: pointer;}
.start{background-color: green;}
.stop{background-color: red;}
.reset{background-color: orange;}
input{width: 140px; height: 50px; line-height: 50px; font-size: 40px; color: blue; text-align: right; border: 2px solid cyan;}
.wow{font-size: 32px; color: red; margin: 10px 0; width: 290px; text-align: center; display: none;}
</style>
<h4>挑战10秒</h4>
<div class="start">start</div>
<div class="stop">stop</div>
<div class="reset">reset</div>
<div></div>
<input class="second" type="text" value="0" readonly="readonly">
<input class="millisecond" type="text" value="0" readonly="readonly">
<div class="wow">你真特么牛逼!!!</div>
<script>
var start = document.getElementsByClassName('start')[0];
var stop = document.getElementsByClassName('stop')[0];
var reset = document.getElementsByClassName('reset')[0];
var second = document.getElementsByClassName('second')[0];
var millisecond = document.getElementsByClassName('millisecond')[0];
var wow = document.getElementsByClassName('wow')[0];
var flag = true;
var timer;
start.addEventListener('click', function(){if(flag){var millitime = 0;var secondtime = 0;timer = setInterval(function(){millitime++;millisecond.value = millitime;if(millitime >= 99){secondtime++;second.value = secondtime;millitime = 0;}}, 10)start.style.backgroundColor = 'gray';start.style.cursor = 'not-allowed';flag = false;}
}, false);
stop.addEventListener('click', function(){clearInterval(timer);if(second.value == 10 && millisecond.value == 0){second.style.borderColor = 'red';millisecond.style.borderColor = 'red';second.style.color = 'red';millisecond.style.color = 'red';wow.innerText = '你真特么牛逼!!!'wow.style.display = 'block';}else{wow.innerText = '你真特么笨!!!';wow.style.display = 'block';}
}, false);
reset.addEventListener('click', function(){clearInterval(timer);second.value = 0;millisecond.value = 0;start.style.backgroundColor = 'green';start.style.cursor = 'pointer';second.style.borderColor = 'cyan';millisecond.style.borderColor = 'cyan';second.style.color = 'blue';millisecond.style.color = 'blue';wow.style.display = 'none';flag = true;
}, false);
</script>

键盘事件

  1. keydown、keyup、keypress
  2. keydown > keypress > keyup
  3. keydown 和 keypress的区别
    keydown可以相应任意键盘按键,keypress只可以相应字符类键盘按键
    keypress返回ASCII码,可以转换成相应字符(String.fromCharCode())
<script>
document.onkeydown = function(e){console.log('keydown', e)};
document.onkeyup = function(){console.log('keyup')};
document.onkeypress = function(e){console.log('keypress', String.fromCharCode(e.charCode))};
</script>

文本操作事件

input、focus、blur、change

<input type="text" value="请输入用户名" style="color: #999" onfocus="if(this.value == '请输入用户名'){this.value = ''}; this.style.color = '#494949'" onblur="if(this.value == ''){this.value = '请输入用户名'; this.style.color = '#999'}">

窗体类操作事件

scroll、 load

<div style="height: 5000px;"></div>
<script>
document.onscroll = function(){console.log(window.pageXOffset + ',' + window.pageYOffset);
};
</script>

json

  1. json是一种传输数据的格式(以对象为样板,本质上就是对象,但用途有区别,对象是本地用的,json是用来传输的)
  2. JSON.parse(); string ——> json
  3. JSON.stringify(); json ——> string

异步加载js

js加载的缺点:加载工具方法没必要阻塞文档,过多的js加载会影响页面效率,一旦网速不好,那整个页站将等待js加载而不进行后续渲染等工作。
有些工具方法需要按需加载,用到再加载,不用不加载。

JavaScript异步加载的三种方案

  1. defer 异步加载,但要等到DOM文档全部解析完才会被执行。只有IE能用,也可以将代码写到内部。(IE用)
  2. async 异步加载,加载完就执行,async只能加载外部脚本,不能把js写在script标签里。(非IE用,IE9以上也可以用)
    1、2执行时不阻塞页面
  3. 创建script,插入到DOM中,加载完毕后callback。(兼容性好,最常用)

例:
demo.js

function test (){console.log('abc');
}

demo.html

<script type="text/javascript">var script = document.createElement('script');script.type = 'text/javascript';script.src = 'demo.js';if(script.readyState){script.onreadystatechange = function(){ //IEif(script.readyState == 'complete' || script.readyState == 'loaded'){test();}}}else{script.onload = function(){ //Safari chrome firefox operatest();}}document.head.appendChild(script);
</script>

优化封装:

function loadScript (url, callback){var script = document.createElement('script');script.type = 'text/javascript';if(script.readyState){script.onreadystatechange = function(){ //IEif(script.readyState == 'complete' || script.readyState == 'loaded'){callback();}}}else{script.onload = function(){ //Safari chrome firefox operacallback();}}script.src = url;  //防止网速过快,瞬间完成下载,而无法触发IE的onreadystatechange,故放在if后面document.head.appendChild(script);
}
//调用
loadScript ( 'demo.js', function(){test();  //fn必须放在匿名函数体里面,防止test被当做变量解析,因为loadScript还没有执行,test尚未定义
})
//常规开发中工具函数库一般封装为对象形式
demo.js
var tools = {test: function(){console.log('abc');},test2: function(){console.log('def');},...
}//封装:
function loadScript (url, callback){var script = document.createElement('script');script.type = 'text/javascript';if(script.readyState){script.onreadystatechange = function(){ //IEif(script.readyState == 'complete' || script.readyState == 'loaded'){tools[callback]();}}}else{script.onload = function(){ //Safari chrome firefox operatools[callback]();}}script.src = url;document.head.appendChild(script);
}
//调用:
loadScript('demo.js', 'test');

js加载时间线

  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. 从此,以异步响应方式处理用户输入、网络事件等。

例:

<script>console.log(document.readyState);document.onreadystatechange = function(){console.log(document.readyState);}//loading//interactive//complete//DOMContentLoaded事件只能在addEventListener上有用document.addEventListener('DOMContentLoaded', function(){console.log('a');}, false)
</script>

RegExp正则表达式

正则表达式作用:匹配特殊字符或有特殊搭配原则的字符的最佳选择。
正则表达式两种创建方式:

  1. 直接量
    var reg = /abc/m;
  2. new RegExp();
    var reg = new RegExp(‘abc’, ‘m’);

推荐使用直接量

var reg = /abc/m;
var reg1 = new RegExp(reg);
//reg和reg1是两个独立的正则表达式
var reg2 = RegExp(reg)
//reg和reg2是同一个正则表达式,reg2是对reg的一个引用

正则表达式的修饰符(属性)

  1. i //忽略大小写
var reg = /ab/;
var str = 'abc';
var str1 = 'Abc';
reg.test(str);      //true
reg.tests(str1);    //false
var reg1 = /ab/i;
reg1.test(str);     //true
reg1.test(str1);    //true
  1. g //全局匹配
var reg = /ab/;
var str = 'abcabcdabcde';
str.match(reg); //['ab']
var reg1 = /ab/g;
str.match(reg1); //['ab', 'ab', 'ab']
  1. m //换行匹配
var reg = /ab/m;
var str = 'abc\nabcd\nabcde';
str.match(reg);         //['ab']
var reg1 = /^ab/m;
str.match(reg1);        //['ab']
var reg2 = /^ab/mg;
str.match(reg2);        //['ab', 'ab', 'ab']

正则表达式中[]代表一位, 在[]外面表示以XX开头,在[]里面表示非,跟!一样; |表示或

var reg = /[ab][cd][d]/g;
var str = 'abcd';
console.log(str.match(reg));    //['bcd']
var reg = /[^a][^b]/g;
var str = 'abcde';
console.log(str.match(reg));        //['bc', 'de']
var reg = /(abc|bcd)[0-9]/g;
var str = 'abcd5';
console.log(str.match(reg));    //['bcd5']

例:将the-first-name转换为theFirstName

<script>
var str = 'the-first-name';
var reg = /-(\w)/g;
console.log(str.replace(reg, function($, $1){return $1.toUpperCase();
}))
</script>

例:去重‘aabbbcccc’

<script>
var str = 'aabbbcccc';
var reg = /(\w)\1*/g;
console.log(str.replace(reg, '$1'))
</script>

例: 千分制计数

<script>
var str = '100000000';
var reg = /(?=(\B)(\d{3})+$)/g;
console.log(str.replace(reg, ' '))
</script>

JavaScript note相关推荐

  1. 一款使用JavaScript实现的篮球投篮游戏源代码,在浏览器里就能玩的篮球小游戏代码

    一款使用JavaScript实现的篮球投篮游戏源代码,在浏览器里就能玩的篮球小游戏代码 -根据光标的移动,沿某个方向击球 -球改变大小以使体验像3D一样 -音效让体验像真实生活! -球与轮辋碰撞并反弹 ...

  2. selenium资料

    来源 http://release.seleniumhq.org/selenium-remote-control/0.9.2/doc/dotnet/Selenium.ISelenium.MouseMo ...

  3. Android 获取WebView的内容宽度高度

    转自:http://www.android100.org/html/201311/19/4804.html Android开发时,从WebView,我不但想要知道ContentHeight,还想知道C ...

  4. 撰写本文的所有基本React.js概念

    Update: This article is now part of my book "React.js Beyond The Basics". 更新:本文现在是我的书<超 ...

  5. json_decode用法

    json_decode (PHP 5 >= 5.2.0, PECL json >= 1.2.0) json_decode - 对 JSON 格式的字符串进行编码 Report a bug ...

  6. JNLP文件详细说明

    Java Web Start 是一个 helper 应用程序,它和 WEB 浏览器关联在一起.当用户点击指向一个特定的launch 文件 (JNLP 文件 ) 时,会促使浏览器装载 java Web ...

  7. JNLP说明(good)

    转自(http://blog.csdn.net/llythh/archive/2009/06/26/4299490.aspx) Java Web Start 是一个 helper 应用程序,它和 WE ...

  8. 达内java笔记_达内java笔记

    J2EE所有的知识点都详细的记录在里面了,浓缩的才是精华,放在手机里随时记一记背一背,这是精挑细选后的成果,在这里0积分奉献给大家. 达内笔记 ├─01. Unix note.txt    101.0 ...

  9. emcc 命令帮助手册

    Emscripten Compiler Frontend (emcc) The Emscripten Compiler Frontend ("emcc") is used to c ...

最新文章

  1. Java中深浅拷贝之List
  2. Android图片处理
  3. CSV格式文件注意细节
  4. D3D API - D3DXCreateRenderToSurface渲染到纹理
  5. 本田crv混动操作手册_【广汽本田】不只是奶爸车 新出行试驾本田奥德赛锐混动...
  6. IOS-C语言第8天,Struct (结构体)
  7. 0-1背包问题详解(DP分支限界回溯三种方法)
  8. 三大缓存框架(Ehcache+Memcache+Redis)基础
  9. JS表单学习笔记(思维导图)
  10. struts1(一)流程分析
  11. 58、IPv6访问控制列表及ICMPv6简介
  12. 在线文本比较工具-toolfk程序员在线工具网
  13. 【 CSDN 】 如何修改CSDM 自定义摘要功能
  14. 保研之路——上科大信息学院直硕夏令营
  15. 电脑程序是如何运行起来的
  16. 彩色图直方图均衡化matlab
  17. 掷骰子java程序_掷骰子游戏窗体实现--Java初级小项目
  18. 通往奥格瑞玛的道路(二分+迪杰斯特拉堆优化)
  19. html5摄像头 在线演示,基于HTML5实现的超酷摄像头(HTML5 webcam)摄
  20. 一元二次函数求解(三角函数、二元一次函数转换为一元二次函数的思路)

热门文章

  1. 信息孤岛的由来,以及如何改善
  2. JAVA和H5的优势有哪些
  3. pytorch安装和tensorflow环境搭建和cuda加速和cudann安装教程记录日期2022.10.20日
  4. 辨别亦真亦假的Svchost.exe
  5. 伴随我们长大的经典—写给从80后的一批人
  6. 网络连通性测试ping和tracert命令
  7. 【前端框架学习】v-for指令和key属性
  8. 卡卷特权接口API源码
  9. 设计模式——行为型模式之责任链模式(简简单单入门责任链,理解I/O流消息怎么逐步传递处理以及服务器框架转发)
  10. 在Proteus 中51单片机常用器件名称和图片