一、函数高级
1.函数回调
函数回调的本质:在一个函数中,满足特定条件下,调用另一个函数
// 回调的函数 function callback(data) {} // 逻辑函数 function func(callback) { // 函数回调 if (callback) callback(data); }
function a_fn(data) { console.log('a_fn'); // 如果要使用数据,那么定义形参接收参数 console.log(data); } function b_fn(a_fn) { var data = "b_fn 的 数据"; console.log('b_fn'); // 条件: a_fn有值(有函数实现体) if (a_fn) { // 调用参数函数 a_fn(data); } } b_fn(a_fn);
总结:
1.一个函数(函数名)作为另外一个函数的参数
2.满足一定的条件,调用参数函数
3.形成了函数回调,回调的函数可以获取调用函数的局部变量(将数据携带出去)
2.闭包
闭包本质:函数的嵌套定义,内层函数称之为闭包
闭包目的:不允许提升变量作用域时,该函数的局部变量需要被其他函数使用
闭包的解决案例:①影响局部变量的生命周期,持久化局部变量;②解决变量污染
function outer() { var data = {} //闭包 function inner() { return data; } return inner; }
注:外部使用局部变量的方法: 返回值 | 函数回调 | 闭包 | 提升作用域
2.1局部变量持久化
function outer() { //eg:请求得到的数据,如果不持久化,方法执行完毕后,数据就会被销毁 var data=[1,2,3,4,5]; console.log(data); //通过闭包解决该类问题,所以闭包所有代码均可以随意自定义 function inner(){ return data; } //数据被inner操作返回,inner属于outer,所以需要将inner返回出去(跟外界建立联系) return inner; } //将局部变量生命周期提升于inner函数相同,inner存在,局部变量data就一直存在 var inner = outer();
2.2闭包解决变量污染
现有一个列表,点击某个时,弹出下标
<script type="text/javascript"> // 需求:点击li,打印li在ul中的索引 => 0, 1, 2, 3, 4 // 1.lis var lis = document.querySelectorAll('ul li'); // 2.循环绑定 for (var i = 0; i < lis.length; i++) { // 解决的原理:一共产生了5个外层函数,存储的形参i的值分别是0, 1, 2, 3, 4 // 内层函数也产生了5个,且和外层函数一一对应,打印的i就是外层函数的形参i // 外层函数 (function (i) { // 内层函数:闭包 lis[i].onclick = function () { alert(i) } })(i) } console.log(i); // 点击事件触发一定晚于该逻辑语句 // 所以再去触发点击,弹出i的值,永远是5 // 该类问题就称之为变量污染 </script>
ES6的块级作用域的语法也可以解决循环绑定变量污染的问题
let lis = document.querySelectorAll('li'); for (let i = 0; i < lis.length; i++) { lis[i].onclick = function () { alert(i) } }
二、JS的面向对象
1.面向对象初始
var obj = {}; | var obj = new Object(); // 属性 obj.prop = "";|obj['name']=""; // 方法 obj.func = function () {} // 删除属性与方法 delete obj.prop delete obj.func
2.类字典结构使用
js中没有字典(键值对存储数据的方式),但可以通过对象实现字典方式存储数据,并使用数据
var dict = {name: "zero", age: 18} var dict = {"my-name": "zero", fn: function () {}, fun () {}} //使用 dict.name | dict["my-name"] | dict.fn()
总结:
1.key全为字符串形式,但存在两种书写方式
2.key在js语法标识符支持情况下,可以省略引号,但key为js非法标识符的情况下,必须添加引号。
3.value可以为任意类型
4.访问值可以通过字典名(对象名).key语法与[“key”]语法来访问value
5.可以随意添加key与value完成增删改查
// 增 dict.key4 = true; console.log(dict); // 删 delete dict.key4; console.log(dict); // 改 dict["my-key3"] = [5, 4, 3, 2, 1]; console.log(dict); // 查 console.log(dict.key1); console.log(dict["key1"]);
3.构造函数
构造函数其实就是普通函数
特定的语法与规定:
1.一般采用首字母大写(大驼峰)
2.内部可以构建属性与方法,通过this关键词
// 普通函数 function fn() { var num = 10; console.log('普通函数'); } fn(); //构造函数 function People(name) { this.name = name; this.eat = function (agr) { console.log(this.name + "吃" + agr); } }
构造函数可以创建多个对象,使用{}构建出的对象是唯一的
var p1 = new People("zero"); // new关键词, 创建对象并实例化 var p2 = new People("seven"); var obj = {} //对象唯一
构造函数的属性变量与{}的语法规则不一样,构造函数内部通过this,二普通对象直接通过对象名.的方式
function People(name) { this.name = name; this.eat = function (agr) { console.log(this.name + "吃" + agr); } } //普通对象 var obj = { index: "obj对象", fn: function () { console.log("obj方法"); } }; // 使用属性与方法 console.log(obj.index); obj.fn();
4.继承
1.完成继承,必须拥有两个构造函数
2.一个函数要使用另外一个函数的属性与方法,需要对其进行继承(属性与方法的复用)
4.1 ES5的继承
属性的继承用call方法,在继承函数中由被继承函数调用,传入继承函数的this与被继承函数创建属性对属性进行赋值的所有需要的数据
方法的继承prototype原型
// 类似于父级 function Sup(name) { // 属性存放某个值 this.name = name; // 方法完成某项功能 this.func = function () { console.log("func"); } } var sup = new Sup("supClass"); console.log(sup.name); sup.func(); // 类似于子级 function Sub(name) { // 属性的继承 Sup.call(this, name);//Sup有name属性,那么可以通过call将Sub的name传给Sup } // 方法的继承 Sub.prototype = new Sup; //将new Sup赋值给Sub.prototype var sub = new Sub("subClass"); console.log(sub.name); sub.func(); // 原型 console.log(sup.__proto__);
4.2 ES6继承
// 多对象 => 创建类 class Person { // 构造器:创建对象完成初始化操作 constructor (name, age) { this.name = name; this.age = age; } // 实例方法:只能由对象调用 eat () { console.log(this.name + '吃吃吃'); } // 类方法:只能由类调用 static create () { console.log('诞生'); } } let p1 = new Person('zero', 8); let p2 = new Person('seven', 58); console.log(p1.age); p2.eat(); // Person.eat(); Person.create(); // p2.create(); // 继承 class Student extends Person { constructor (name, age, sex) { // super指向父级 super(name, age); this.sex = sex; } } let s1 = new Student("张三", 18, "男"); // 属性的继承 console.log(s1.name, s1.age, s1.sex); console.log(); // 方法的继承 s1.eat(); // 继承为全继承 Student.create();
补:ES6用箭头语法表示函数
// 函数 let f = () => { }
5.属性解决循环绑定变量污染
还是用之前的变量污染的例子,js代码看下面
var lis = document.querySelectorAll('li'); for (var i = 0; i < lis.length; i++) { // lis[i]依次代表五个li页面元素对象 // 给每一个li设置一个(临时)属性 lis[i].index = i; // 第一个li的index属性存储的就是索引0,以此类推,最后一个存储的是索引4 lis[i].onclick = function () { // var temp = lis[i].index; // lis[i]中的i一样存在变量污染 var temp = this.index; // 当前被点击的li alert(temp) // temp => this.index(lis[i].index) => i(索引) } }
三、定时器
应用场景:
1.完成js自启(不需要手动触发)动画
2.css完成不了的动画
-
-
setTimeout 持续性定时器
// 一次性定时器 /* 异步执行 setTimeout(函数, 毫秒数, 函数所需参数(可以省略)); */ console.log("开始"); setTimeout(function (data) { console.log(data); }, 1000, "数据"); console.log("结束"); // 持续性定时器 /*异步执行 setInterval(函数, 毫秒数, 函数所需参数(可以省略)); */ console.log("又开始"); var timer = setInterval(function() { console.log("呵呵"); }, 1000) console.log(timer); console.log("又结束");
清除定时器
1.持续性与一次性定时器通常都应该有清除定时器操作
2.清除定时器操作可以混用 clearTimeout() | clearInterval()
3.定时器的返回值就是定时器编号,就是普普通通的数字类型
document.onclick = function () { console.log("定时器清除了"); clearInterval(timer); // clearTimeout(timer); } // 清除所有定时器 // 如果上方创建过n个定时器,那么timeout值为n+1 var timeout = setTimeout(function(){}, 1); for (var i = 0; i < timeout; i++) { // 循环将编号[0, n]所有定时器都清除一次 clearTimeout(i) } // 1.允许重复清除 // 2.允许清除不存在的定时器编号