继承
对象拷贝
for......in :父对象的属性拷贝给子对象。
// 父级的对象
var laoli = {
name: "laoli",
money: 1000000,
house: ["商铺", "住宅"],
tech: function () {
console.log("厨艺")
}
};
// 子级的对象
var xiaoli = {
name: "xiaoli"
}
// 封装一个对象之间继承的函数
function extend(parent, child) {
for (var k in parent) {
// 子级有的属性不需要继承
if (child[k]) {
continue;
}
child[k] = parent[k];
}
}
// 调用函数实现继承
extend(laoli,xiaoli);
console.log(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);
Call方法
// 人类类型
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;
}
// 创建学生的实例对象
var s1 = new Student("zs",18,"男",89);
console.dir(s1);
组合继承
// 组合继承:属性在构造函数内部继承,方法通过原型继承
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();
函数定义方式
函数声明
//必须定义函数名
function fun() {
console.log(1);
}
函数表达式
// 是将函数赋值给一个变量,可以是一个匿名函数
var fn = function () {
console.log(2);
};
new Function
// 通过构造函数方法定义函数
// 函数本身也是一种对象
var fun = new Function('a','b','var a = "1";console.log(a+b)');
fun(2,3);
console.dir(fun);
函数声明与函数表达式的区别
- 函数声明必须有名字
- 函数声明会函数提升,在预解析阶段就已创建,声明前后都可以调用
- 函数表达式类似于变量赋值
- 函数表达式可以没有名字,例如匿名函数
- 函数表达式没有函数提升,在执行阶段创建,必须在表达式执行之后才可以调用
函数的调用方法
普通函数
- 通过给函数名或者变量名添加()方式执行
- 内部的 this 默认指向 window
function fun() {
console.log(this);
}
fun();
构造函数
- 是通过 new 关键字进行调用
- 内部的 this 指向的是将来创建的实例对象
function Person(name) {
this.name = name;
console.log(this);
}
var p1 = new Person("zs");
Person();
对象中的方法
- 是通过对象打点调用函数,然后加小括号
- 内部的 this 默认指向的是调用的对象自己
var o = {
sayHi: function () {
console.log("haha");
},
fn: fun
}
// this 的指向是要联系执行的上下文,在调用的时候,是按照什么方式调用,指向是不一样的
o.sayHi();
事件函数
- 不需要加特殊的符号,只要事件被触发,会自动执行函数
- 事件函数的内部 this 指向的是事件源
document.onclick = function () {
console.log("点击事件");
};
定时器和延时器中的函数
- 不需要加特殊的符号,只要执行后,在规定的时间自动执行
- 默认内部的 this 指向的是 window
setInterval(function () {
console.log("time");
},1000);
函数内 this 指向的不同场景
- 函数的调用方式决定了 this 指向的不同:
| 调用方式 | 非严格模式 | 备注 |
|---|---|---|
| 普通函数调用 | window | 严格模式下是underfined |
| 构造函数调用 | 实例对象 | 原型方式中this也是实例对象 |
| 对象方法调用 | 该方法所属对象 | 紧挨着的对象 |
| 事件绑定方法 | 绑定事件对象 | |
| 定时器函数 | window |
call、apply、bind
call
- call() 方法调用一个函数, 其具有一个指定的 this 值和分别地提供的参数(参数的列表)。
- 注意:该方法的作用和 apply() 方法类似,只有一个区别,就是 call() 方法接受的是若干个参数的列表,而 apply() 方法接受的是一个包含多个参数的数组。
- 语法:
fun.call(thisArg,arg1, arg2, arg3, ...) - thisArg
在 fun 函数运行时指定的 this 值
如果指定了 null 或者 undefined 则内部 this 指向 window - arg1, arg2, ...
指定的参数列表
// {} 的对象自己是没有 push 方法的
// 类数组对象 getElementsByTagName
var o = {
0: 10,
1: 20,
2: 30,
length: 3
};
// 利用数组中的 push 方法,指定内部的this 为对象 o,就可以处理类数组对象的数据
Array.prototype.push.call(o,50);
console.log(o);
apply
- apply() 方法调用一个函数, 第一个参数是一个指定的 this 值,第二个参数是以一个数组 (或类似数组的对象)形式提供的参数。
- 注意:该方法的作用和 call() 方法类似,只有一个区别,就是 call() 方法接受的是若干个参数的列表,而 apply() 方法接受的是一个包含多个参数的数组。
- 语法:
fun.apply(thisArg, [argsArray])
// apply 方法可以指定一个函数的 this,并且通过数组方式进行传参
// fun.apply(this,[1,2]);
// 定义一个数组,利用 apply 方法,可以将它拆开进行操作
var arr = [1,3,4,6,8];
// 想借用一些现在内置在js 中的方法
console.log(Math.max(1,3,5,7,9));
// 利用 apply 方法,将数组传给 max 的第二个参数
console.log(Math.max.apply(Math,arr));
console.log(1,2,3);
console.log.apply(console,arr);
bind
-
bind() 函数会创建一个新函数(称为绑定函数),新函数与被调函数(绑定函数的目标函数) 具有相同的函数体(在 ECMAScript 5 规范中内置的 call 属性)。
-
当目标函数被调用时 this 值绑定到 bind() 的第一个参数,该参数不能被重写。绑定函数被 调用时,bind() 也接受预设的参数提供给原函数。
-
一个绑定函数也能使用 new 操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。
-
语法:
fun.bind(thisArg,arg1, arg2, arg3, ...) -
参数:
-
thisArg:当绑定函数被调用时,该参数会作为原函数运行时的 this 指向。当使用 new 操作符调用绑定函数时,该参数无效。
-
arg1, arg2, ...:当绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法。
-
返回值:
返回由指定的 this 值和初始化参数改造的原函数拷贝。
// 想修改的是定时器的函数内部的 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);
总结
call 和 apply 特性一样
- 都是用来调用函数,而且是立即调用
- 但是可以在调用函数的同时,通过第一个参数指定函数内部 this 的指向
- call 调用的时候,参数必须以参数列表的形式进行传递,也就是以逗号分隔的方式依次传递即可
- apply 调用的时候,参数必须是一个数组,然后在执行的时候,会将数组内部的元素一个一个拿出来,与形参一一对应进行传递
- 如果第一个参数指定了 null 或者 undefined 则内部 this 指向 window
bind
- 可以用来指定内部 this 的指向,然后生成一个改变了 this 指向的新的函数
- 它和 call、apply 最大的区别是:bind 不会调用
- bind 支持传递参数,它的传参方式比较特殊,一共有两个位置可以传递
1. 在 bind 的同时,以参数列表的形式进行传递
2. 在调用的时候,以参数列表的形式进行传递 - 那到底以谁 bind 的时候传递的参数为准呢还是以调用的时候传递的参数为准
两者合并:bind 的时候传递的参数和调用的时候传递的参数会合并到一起,传递到函数内部
函数的其他成员
- 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);
}
// 灵活使用 arguments 类数组对象,可以记录所有的实参
// 模仿制作一个max方法
function max() {
// 判断实参中最大的数
var nowMax = arguments[0];
for (var i = 1 ; i < arguments.length;i++) {
if (arguments[i] > nowMax) {
nowMax = arguments[i];
}
}
return nowMax;
}
console.log(max(1,4,7,9));
高阶函数
- 函数可以作为参数
//.函数作为另一个函数的参数
// 定义一个函数,吃饭的函数,吃完饭之后,可以做其他的事情,看电影、聊天、看书
function eat(fn) {
console.log("吃晚饭");
// 接下来的要做的事情是不固定的
fn();
}
eat(function () {
console.log("看电影");
});
- 函数可以作为返回值
// 函数作为一个函数的返回值
// 需求:通过同一段代码实现以下效果
// 输出 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]();