JavaScript note

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.下列方法后面不用加分号(;) 【JavaScript note】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的索引
  • 1
  • 2
  • 3
  • 4
  • 5
错误: 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(不报错)varnum = 3; num.len = 5; //此时刷新页面,执行不会报错 console.log(num.len); //undefined(不报错)

以上代码执行时有一过程叫——包装类,如下:
//包装类 varnum = 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,获取子元素节点


例: 属性节点

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

DOM结构树
JavaScript note
文章图片

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,返回自己
abc


  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

日期对象Date() 例:秒表
input{ border: 1px solid rgba(0, 0, 0, 0.8); text-align: right; font-size: 20px; font-weight: bold; }minutes: seconds:

脚本化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)

window.getComputedStyle(ele, ull)可以获取伪元素的css, null传伪元素
.div1::after{ content: ''; width: 20px; height: 20px; background-color: yellow; display: inline-block; }

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]; } }

例:操作伪元素思想
.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; }

例: 滑动小方块

事件 如何绑定事件处理函数
  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)

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

例:ele.addEventListener(type, fn, false)
li{width: 100px; height: 30px; background-color: orange; line-height: 30px; font-size: 16px; margin: 5px 0; text-align: center; color: #ffffff; }
  • abc
  • abc
  • abc
  • abc

事件处理程序的运行环境
  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
abc

例:封装兼容性的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等事件不冒泡。
一个对象的一个事件类型只能遵循一种事件处理模型,要么冒泡,要么捕获。
例:冒泡
.red{width: 300px; height: 300px; background-color: red; } .orange{width: 200px; height: 200px; background-color: orange; } .yellow{width: 100px; height: 100px; background-color: yellow; }

例: 捕获
.red{width: 300px; height: 300px; background-color: red; } .orange{width: 200px; height: 200px; background-color: orange; } .yellow{width: 100px; height: 100px; background-color: yellow; }

例: 捕获和冒泡的触发顺序
.red{width: 300px; height: 300px; background-color: red; } .orange{width: 200px; height: 200px; background-color: orange; } .yellow{width: 100px; height: 100px; background-color: yellow; }

取消冒泡和阻止默认事件 取消冒泡
  1. W3C标准 event.stopPropagation(); 但不支持IE9以下版本
  2. IE独有 event.cancelBubble = true; (谷歌已实现兼容)
  3. 封装兼容性的取消冒泡函数 stopBubble(event)
    obj.onlcick = function(e){} e中包含了事件发生时一系列的属性,记录事件发生时的一系列状态和信息。
    例: 封装兼容性函数取消冒泡事件stopBubble(event)

阻止默认事件
默认事件——表单提交、a标签跳转、右键菜单等
  1. return false; 以对象属性方式(句柄方式)注册的事件才生效
  2. event.preventDefault(); W3C标准,IE9以下不兼容
  3. event.returnValue = https://www.it610.com/article/false; 兼容IE
  4. 封装兼容性的阻止默认事件的函数cancelHandler(event);
    例: 封装兼容性的阻止默认事件的函数cancelHandler(event)

事件对象 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的内容
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

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

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

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

例:区分click和mousedown

例:10秒小游戏
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; }挑战10秒 start stop reset 你真特么牛逼!!!

键盘事件
  1. keydown、keyup、keypress
  2. keydown > keypress > keyup
  3. keydown 和 keypress的区别
    keydown可以相应任意键盘按键,keypress只可以相应字符类键盘按键
    keypress返回ASCII码,可以转换成相应字符(String.fromCharCode())

文本操作事件 input、focus、blur、change

窗体类操作事件 scroll、 load

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

优化封装:
function loadScript (url, callback){ var script = document.createElement('script'); script.type = 'text/javascript'; if(script.readyState){ script.onreadystatechange = function(){ //IE if(script.readyState == 'complete' || script.readyState == 'loaded'){ callback(); } } }else{ script.onload = function(){ //Safari chrome firefox opera callback(); } } script.src = https://www.it610.com/article/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(){ //IE if(script.readyState == 'complete' || script.readyState == 'loaded'){ tools[callback](); } } }else{ script.onload = function(){ //Safari chrome firefox opera tools[callback](); } } script.src = https://www.it610.com/article/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. 从此,以异步响应方式处理用户输入、网络事件等。
例:

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

例:去重‘aabbbcccc’

例: 千分制计数

    推荐阅读