文章目录
继承和函数进阶
继承
对象之间的继承
-
通过for…in实现对象间的拷贝
//继承的函数 function extend(parent, child) { for (var k in parent) { if (child[k]) { continue; } child[k] = parent[k]; } }
原型继承
-
可以将公共有的样式单独列出来,通过实例对象可以继承原型对象属性,给原型对象进行添加
// 原型对象,可以将自己的属性和方法继承给将来的实例对象 Student.prototype = new Person("zs", 18, "男"); // 生成实例 var s1 = new Student(89); var s2 = new Student(100); console.log(s1); console.log(s1.constructor); //Person函数 Student.prototype.constructor = Student; console.log(s1.constructor); //Student函数
- 传参只能传一次
- 其中的construtor属性需要手动修改
函数的call方法
-
更改函数内部的this指向
-
调用函数执行内部代码,第一个给的是this的指向,从第二个开始传入的是实参
function fn(a, b) { console.log(this); //{name: "zs"} console.log(a + b); //6 } var o = { name: "zs" } fn.call(o, 2, 4)
借用构造函数继承属性
-
与call方法结合,通过更改this指向问题可以解决原型继承中只能传入一个参数的问题
function Student(name, age, sex, score) { // 直接对父类型的构造函数进行一个普通调用 // Person 普通调用过程中,内部的 this 指向的是 window // 可以通过 call 方法更改Person 内部的 this Person.call(this, name, age, sex); this.score = score; }
构造函数方法的继承
-
方法写入到原型对象之上就可以节省空间
-
方法的拷贝(for…in)继承
// 通过拷贝方法子原型继承父原型方法 for (var k in Person.prototype) { // 需要保留自己的construtor属性 if (k === "construtor") { continue; } Student.prototype[k] = Person.prototype[k]; }
-
原型继承
// 将子原型直接指向父的实例,就可以继承父原型中的方法 Student.prototype = new Person(); // 保留construtor,返回结果construtor: ƒ Student(name, age, sex, score) Student.prototype.construtor = Student; //返回的原型是__proto__: Person
组合继承
-
属性在构造函数内部继承,方法通过原型继承
function Teacher(name, age, sex, salary) { // 属性的继承通过构造函数 Person.call(this, name, age, sex); this.salary = salary; } // 方法的继承通过原型 Teacher.prototype = new Person(); Teacher.prototype.construtor = Teacher; var t1 = new Teacher("li_sir", 45, "男", 10000);
函数进阶
函数声明和函数表达式
-
函数声明
- 必须定义函数名
- f1()调用写在前后都行,因为有函数声明提升
function f1() { console.log(1); }
-
函数表达式
- 将函数赋值给一个变量,也可以是一个匿名函数
- 如果在前面调用,返回的只是一个undefined的变量
var f2 = function() { console.log(2); }
-
if语句中添加函数,容易产生高低版本不兼容,结果一致的现象,我们可以直接使用变量定义函数
-
对象方法
var fn = new Function('a', 'b', 'var a="1";console.log(a+b)'); fn(2, 3); //13字符串
函数调用和this
-
普通函数的调用通过函数名或者变量名()方法执行
// 1、普通函数 function fun() { console.log(1); } fun();
-
构造函数通过new关键字调用
// 2、构造函数 function Person(name) { this.name = name; } var p1 = new Person("zs");
-
对象中的方法通过打点调用,然后加小括号
// 3、对象中的方法,打点调用 var o = { sayhi: function() { console.log("hello"); } } o.sayhi();
-
事件函数,触发事件则执行
// 4、事件函数,被触发则执行 document.onclick = function() { console.log("事件"); };
-
定时器或延时器函数,在规定时间后执行
// 5、定时器或延时器中函数,一段时间后执行函数 setInterval(function() { console.log("timer"); }, 100)
-
this指向问题,在不同的执行环境默认指向会改变
call、apply、bind方法
解决函数内部调用时this指向的问题
-
call方法
-
可以指定函数的 this
-
可以执行函数传参,第一个参数传入一个指定的this更改对象,第二个参数即以后都是函数参数列表
function fun(a, b) { console.log(this); console.log(a + b); } var o = { name: "zs" } fun.call(o, 1, 2); //{name: "zs"},this指向了o
-
作用,{}的对象自己是没有Array的方法,我们可以将数组Array的原型对象的this指向更改为我们自己定义的函数,就可以处理类数组对象的数据
Array.prototype.push.call(o, 60); console.log(o); //{0: 10, 1: 20, 2: 30, 4: 60, length: 4}
-
-
apply方法
-
可以修改this的指向
-
也可以进行函数的传参,所有的参数写在一个数组中
fun.apply(o, [3, 4]);
-
可以指定一个函数的this,并且通过数组的方法进行传参
var arr = [1, 3, 7, 4, 8]; console.log(Math.max.apply(null, arr)); //8 console.log.apply(console, arr); //可以将数组打散进行输出1 3 7 4 8
-
-
bind方法
-
可以修改this的指向
-
可以传参,但是不执行函数,返回一个新的制定了this的函数,也可以叫绑定函数
var fn = fun.bind(o, 2, 3); fn();
-
只想修改this的指向,不想执行函数
// 更改定时器内部函数的this var o = { age: 18, s: function() { setInterval(function() { console.log(this.age); }.bind(this), 1000); } } o.s(); //undefined o.s(); //添加this后指向的是18 // 更改事件的触发对象 document.onclick = function() { console.log(this); //#document }.bind(o); //{age: 18, s: ƒ}
-
函数的其他成员
-
arguments:存储的是函数在调用时,传入的所有实参组成的一个类数组对象,实际应用直接使用arguments关键字
- 可以灵活使用arguments类数组对象,可以记录所有的实参
- arguments.callee函数本身,arguments的一个属性
-
caller:函数的调用者,函数在那个作用域中调用,caller就是谁,如果在全局调用,值就是null
-
length:形参的个数
-
name:函数的名字
function fn(a, b) { console.log(fn.arguments); //Arguments(4) [1, 2, 3, 4, callee: ƒ, Symbol(Symbol.iterator): ƒ] console.log(fn.caller); //null console.log(fn.length); //2 console.log(fn.name); //fn,形参个数 } fn(1, 2, 3, 4);
高阶函数
-
函数可以作为另一个函数的参数
function eat(fn) { console.log("吃晚饭"); fn(); } eat(function() { console.log("看电影"); })
-
函数可以作为返回值
// 实现两个加数m、n均可以随意传入参数 function outer(n) { return function inner(m) { console.log(m + n); } } var fun = outer(100); fun(3); //103 fun(23); //123 var fun = outer(1000); fun(3); //1003
闭包
概念
-
一个函数和其周围的引用捆绑在一起
-
记住自己生成的作用域环境和函数自己,将它们形成一个密闭的环境,无论函数以任何方式在任何地方进行调用,都会回到自己定义时的密闭环境进行执行
function outer() { var a = 10; function inner() { console.log(a); } return inner; //将inner作为函数返回值 } var inn = outer(); //全局下定义的inn,按道理返回的是全局的a变量 inn(); //10,返回的是自己闭包环境中的a变量的值
理解和应用
-
闭包是天生存在的,可以记住内部作用环境的变量,函数就是一个闭包、里面的变量并不是一成不变的
-
闭包内部的变量可以被inner在任何地方被调用
-
可以在函数外部读取函数内部成员,让函数内成员始终活在内存中
-
在外部想了一个方法,把构造函数传到外面,指针只想就可以调用内部的函数
问题
-
闭包内部函数无法记住自己当前的值并返回
-
我们可以使用自调用函数来封闭作用域,变量作为局部变量使用
var arr = []; for (var i = 0; i <= 10; i++) { // 使用自调用函数 (function(i) { arr[i] = function() { console.log(i); } })(i); } arr[0](); //11 0 arr[1](); //11 1
-
闭包内部函数无法记住自己当前的值并返回
-
我们可以使用自调用函数来封闭作用域,变量作为局部变量使用
var arr = []; for (var i = 0; i <= 10; i++) { // 使用自调用函数 (function(i) { arr[i] = function() { console.log(i); } })(i); } arr[0](); //11 0 arr[1](); //11 1