继承
对象继承
对象拷贝 for…in
// 封装一个对象之间继承的函数
function extend(parent, child) {
for (var k in parent) {
// 子级有的属性不需要继承
if (child[k]) {
continue;
}
child[k] = parent[k];
}
}
// 调用函数实现继承
extend(laoli,xiaoli);
原型继承
// 封装的构造函数就是用来创建一类对象
// 继承指的是 类型 和 类型之间的继承
// 人类类型
function Person(name,age,sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
// 学生类型
function Student(score) {
this.score = score;
}
// 老师类型
function Teacher(salary) {
this.salary = salary;
}
// 原型对象,可以将自己的属性和方法继承给将来的实例对象使用
Student.prototype = new Person("zs",18,"男");
Student.prototype.constructor = Student;
// 生成一个实例
var s1 = new Student(89);
var s2 = new Student(100);
console.dir(s1);
console.dir(s2);
console.log(s1.name);
console.log(s1.constructor);
构造函数继承(属性和方法)
属性
// 构造函数的属性的继承
// 人类类型
function Person(name,age,sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
// 学生类型
function Student(name,age,sex,score) {
// 直接对父类型的构造函数进行一个普通调用
// Person 普通调用过程中,内部的 this 指向的是 window
// 可以通过 call 方法更改Person 内部的 this
Person.call(this,name,age,sex);
this.score = score;
}
// 老师类型
function Teacher(name,age,sex,salary) {
Person.call(this,name,age,sex);
this.salary = salary;
}
// 创建学生的实例对象
var s1 = new Student("zs",18,"男",89);
var s2 = new Student("ls",19,"男",92);
console.dir(s1);
console.dir(s2);
方法
// 构造函数的属性的继承
// 人类类型
function Person(name,age,sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
// 父类型的原型对象中有方法也需要继承
Person.prototype.sayHi = function () {
console.log("你好");
};
// 学生类型
function Student(name,age,sex,score) {
Person.call(this,name,age,sex);
this.score = score;
}
Student.prototype = new Person();
Student.prototype.constructor = Student;
// 创建学生的实例对象
var s1 = new Student("zs",18,"男",89);
var s2 = new Student("ls",19,"男",92);
s1.sayHi();
组合继承
属性在构造函数内部继承,方法通过原型继承
// 组合继承:属性在构造函数内部继承,方法通过原型继承
function Person(name,age) {
this.name = name;
this.age = age;
}
Person.prototype.sayHi = function () {
console.log("你好");
}
// 生成一个子类型
function Teacher(name,age,salary) {
// 继承父类的属性
Person.call(this,name,age);
this.salary = salary;
}
// 方法继承,通过原型对象继承
Teacher.prototype = new Person();
Teacher.prototype.constructor = Teacher;
// 生成老师的一个实例
var t1 = new Teacher("wang",45,10000);
console.dir(t1);
console.log(t1.name);
t1.sayHi();
函数
函数定义方式
• 函数声明
• 函数表达式
• new Function
函数声明与函数表达式的区别
• 函数声明必须有名字
• 函数声明会函数提升,在预解析阶段就已创建,声明前后都可以调用
// 函数声明提升
fun();
fn();
// 函数声明
// 必须定义函数名
function fun() {
console.log(1);
}
• 函数表达式类似于变量赋值
• 函数表达式可以没有名字,例如匿名函数
• 函数表达式没有函数提升,在执行阶段创建,必须在表达式执行之后才可以调用
• 函数本身也是一种对象,可以调用属性和方法
var fn = function () {
console.log(2);
};
fn();
// 通过构造函数方法定义函数
// 函数本身也是一种对象
var fun = new Function('a','b','var a = "1";console.log(a+b)');
fun(2,3);
console.dir(fun);
函数的调用和this
调用
- 普通函数
// 1. 普通的函数,是通过 给函数名或者变量名添加 () 方式执行
// 内部的 this 默认指向 window
function fun() {
console.log(this);
}
fun();
- 构造函数
// 2.构造函数,是通过 new 关键字进行调用
// 内部的 this 指向的是将来创建的实例对象
function Person(name) {
this.name = name;
console.log(this);
}
var p1 = new Person("zs");
Person();
- 对象方法
// 3.对象中的方法,是通过对象打点调用函数,然后加小括号
// 内部的 this 默认指向的是调用的对象自己
var o = {
sayHi: function () {
console.log("haha");
},
fn: fun
}
// this 的指向是要联系执行的上下文,在调用的时候,是按照什么方式调用,指向是不一样的
o.sayHi();
- 事件函数
// 4.事件函数,不需要加特殊的符号,只要事件被触发,会自动执行函数
// 事件函数的内部 this 指向的是事件源
document.onclick = function () {
console.log("事件");
};
- 定时器、延时器的函数
// 5.定时器和延时器中的函数,不需要加特殊的符号,只要执行后,在规定的时间自动执行
// 默认内部的 this 指向的是 window
setInterval(function () {
console.log("time");
},1000);
this
call、apply、bind
call
• 语法:
fun.call(thisArg,arg1, arg2, arg3, …)
• thisArg
在 fun 函数运行时指定的 this 值
如果指定了 null 或者 undefined 则内部 this 指向 window
• arg1, arg2, … 指定的参数列
• .功能
第一个可以指定函数的 this,第二个可以执行函数并传参
• 返回值:就是函数自己的返回值
apply
• 语法:
fun.apply(thisArg, [argsArray]
• .功能
第一个可以指定函数的 this,第二个可以执行函数并传参
• 返回值:就是函数自己的返回值
bind
• 语法:
fun.bind(thisArg,arg1, arg2, arg3, …)
• thisArg:当绑定函数被调用时,该参数会作为原函数运行时的 this 指向。当使用 new 操作符调用绑定函数时,该参数无效。
• arg1, arg2, …:当绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法。
• .功能
第一个可以指定函数的 this,bind 方法不能执行函数,但是可以传参
• 返回值:
返回由指定的 this 值和初始化参数改造的原函数拷贝(返回一个新的指定了 this 的函数,也可以叫绑定函数)
bind() 函数会创建一个新函数(称为绑定函数),新函数与被调函数(绑定函数的目标函数)具有相同的函数体(在 ECMAScript 5 规范中内置的 call 属性)。 当目标函数被调用时 this 值绑定到 bind() 的第一个参数,该参数不能被重写。绑定函数被调用时,bind() 也接受预设的参数提供给原函数。
一个绑定函数也能使用 new 操作符创建对象:这种行为就像把原函数当成构造器。提供的this 值被忽略,同时调用时的参数被提供给模拟函数。
总结
call 和 apply 特性一样
• 都是用来调用函数,而且是立即调用
• 但是可以在调用函数的同时,通过第一个参数指定函数内部 this 的指向
• call 调用的时候,参数必须以参数列表的形式进行传递,也就是以逗号分隔的方式依次传递即可
• apply 调用的时候,参数必须是一个数组,然后在执行的时候,会将数组内部的元素一个一个拿出来,与形参一一对应进行传递
• 如果第一个参数指定了 null 或者 undefined 则内部 this 指向window
bind
• 可以用来指定内部 this 的指向,然后生成一个改变了 this 指向的新的函数
• 它和 call、apply 最大的区别是:bind 不会调用
• bind 支持传递参数,它的传参方式比较特殊,一共有两个位置可以传递
- 在 bind 的同时,以参数列表的形式进行传递
- 在调用的时候,以参数列表的形式进行传递
• 那到底以谁 bind 的时候传递的参数为准呢还是以调用的时候传递的参数为准
---- 两者合并:bind 的时候传递的参数和调用的时候传递的参数会合并到一起,传递到函数内部
应用
var o = {
0: 10,
1: 20,
2: 30,
length: 3
};
Array.prototype.push.call(o,50);
console.log(o);
var arr = [1,3,4,6,8];
console.log(Math.max.apply(Math,arr));
console.log.apply(console,arr);
// 想修改的是定时器的函数内部的 this
var o = {
name: "zs",
age: 18,
s: function () {
setInterval(function () {
console.log(this.age);
}.bind(this),1000);
}
}
// o.s();
// 更改 事件函数中的 this
document.onclick = function () {
console.log(this);
}.bind(o);
函数的其他成员
• arguments 实参集合
• arguments.callee 函数本身,arguments的一个属性
• fn.caller 函数的调用者,如果在全局调用,返回的调用者为 null。
• fn.length 形参的个数
• fn.name 函数的名称
function fn(a,b) {
// 实际应用中,会在函数内部直接使用 一个 arguments 的关键字
console.log(arguments);
// console.log(arguments.callee);
// 存储的是函数在调用时,传入的所有 实参 组成的一个类数组对象
console.log(fn.arguments);
// 函数的调用者,函数在哪个作用域调用,caller 就是谁,如果在全局调用,值就是 null
console.log(fn.caller);
// length 指的是形参的个数
console.log(fn.length);
// 函数的名字
console.log(fn.name);
}
高阶函数
函数可以作为参数
// 1.函数作为另一个函数的参数
// 定义一个函数,吃饭的函数,吃完饭之后,可以做其他的事情,看电影、聊天、看书
function eat(fn) {
console.log("吃晚饭");
// 接下来的要做的事情是不固定的
fn();
}
eat(function () {
console.log("看电影");
});
函数可以作为返回值
// 2.函数作为一个函数的返回值
// 需求:通过同一段代码实现以下效果
// 输出 100 + m
// 输出 1000 + m
// 输出 10000 + m
function outer(n) {
return function inner(m) {
console.log(m + n);
}
}
// 在外部执行 inner 函数
// 100 + m
var fun = outer(100);
fun(3);
fun(13);
fun(23);
var fun1 = outer(1000);
fun1(3);
函数闭包
回顾作用域、作用域链、预解析
全局作用域
函数作用域
没有块级作用域
内层作用域可以访问外层作用域,反之不行
闭包
一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure)。
也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。
在 JavaScript 中,每当创建一个函数,闭包就会在函数创建的同时被创建出来。
函数定义时天生就能记住自己生成的作用域环境和函数自己,将它们形成一个密闭的环境,这就是闭包。
不论函数以任何方式在任何地方进行调用,都会回到自己定义时的密闭环境进行执行。
// 体会闭包
// 将一个内部函数拿到父函数的外面,观察是否还能调用父函数内部的变量
function outer() {
// 形成闭包环境中的变量不是一成不变的,可以被更改
var a = 10;
function inner() {
console.log(a++);
}
// 将inner 函数作为返回值
return inner;
}
var inn = outer();
inn();
inn();
用途
可以在函数外部读取函数内部成员
让函数内成员始终存活在内存中
// 给数组中的每一项赋值一个 函数
var arr = [];
for (var i = 0 ; i <= 10 ; i++) {
// 自调用函数
(function (i) {
arr[i] = function () {
console.log(i);
};
})(i);
}
// 目的:调用数组对应的项,输出它的对应下标
arr[0]();
arr[1]();
arr[2]();