一、Function类:函数类
- 所有的函数(类、普通函数)都是 Function 的一个实例
内置类:
// Array Number String Boolean Object Date Function 等
console.log(typeof Array); // function
console.log(typeof Number); // function
- 内置类和 Function 类
内置类都是 Function 类实例,而实例都有一个 proto**** 指向所属类的原型对象
js 中所有的函数都是 Function 的实例,那么内置类,如 Array 是函数,所以 Array 也是 Function 的实例;
console.log(Array instanceof Function);
既然是实例,那么一定也会有原型关系
console.dir(Array); // 通过打印发现 Array 也是一个对象,它也有 __proto__,根据原型关系,Array 的 __proto__ 应该指向的是 Function 的 prototype
Array.__proto__ === Function.prototype; true
console.log(Date instanceof Function); true
console.log(Date.__proto__ === Function.prototype); true
console.log(RegExp instanceof Function); true
console.log(RegExp.__proto__ === Function.prototype); true
console.log(Object instanceof Function); true
console.log(Object.__proto__ === Function.prototype); true
console.log(Function.prototype.__proto__ === Object.prototype); true
- 因为 Object 也是一个类,所以也是一个函数,所以也是 Function 的实例.所以 Object.proto__ 指向Function.prototype
- 而 Function 本身也是一个类,也是一个函数,所以 Function 也有 prototype,而 prototype 也是一个对象,所以 Function.prototype.proto**** 又指向了 Object.prototype
Function 和 Object 的关系
- Object.proto 指向 Function.prototype
console.log(Object.__proto__ === Function.prototype);
- Function.prototype.proto 指向 Object.prototype
console.log(Function.prototype.__proto__ === Object.prototype);
- 所有的函数都是 Function 的实例
console.log(Array instanceof Function);
- 所有的引用数类型(普通对象、实例对象、函数、类、数组、Date)的都是 Object 这个基类的实例,所以函数也是对象;
console.log(Function instanceof Object);
console.log(Array instanceof Object);
console.log(Date instanceof Object);
let obj = {
id: 1
};
console.log(obj instanceof Object);
function fn() {
console.log('fn')
}
fn.name = '珠峰';
fn.age = 10;
console.log(fn.age);
console.log(fn.age);
总结
- 所有的函数数据类型都是 Function 的实例
function Fn() {}
console.log(Fn instanceof Function); // true
- Function 函数类 本身也是一个函数
console.log(typeof Function); // function
console.log(Function instanceof Function); // true 所以 Function 也是自己的的一个实例
- 因为 Function 是自己的实例,所以 Function.proto 指向自己的 prototype
console.log(Function.__proto__ === Function.prototype);
- Function 也是 Object 基类的实例:所以函数也是对象,可以有自己的私有属性
console.log(Function instanceof Object); // true
- js 的内置引用类型都是 Function 的实例
console.log(Object instanceof Function);
console.log(Object.__proto__ === Function.prototype)
二、函数的三种角色
函数的三种角色:
- 作为一个普通函数执行(形参、实参、返回值)
- 作为一个类(new Fn 构造函数执行)
- 函数也是一个普通对象(通过 .属性名 或者 [‘属性名’] 获取私有属性);
1. 普通函数
function sum(a, b) {
var x = 1;
var y = 12;
var z = 123;
return a + b + x + y + z;
}
var result = sum(1, 3);
console.log(result);
普通函数的执行过程:
- 开辟一个新的作用域
- 形参赋值
- 变量提升
- 函数体从上到下执行
- 销毁作用域
2. 构造函数(类):
- 2.1 每个构造函数都有一个 prototype 属性,它的值是一个对象,用来存放当前类型的公有的属性和方法
- 2.2 必须通过 new 操作符调用函数才能返回一个实例对象;
function Teacher(n, a, s, f) {
// 通过 this.xxx = xxx 给实例对象添加私有属性
this.name = n;
this.age = a;
this.subject = s;
this.from = f;
}
- 在原型上增加的方法都是这个类型公有的属性和方法
Teacher.prototype.teach = function () {
console.log(`${this.name} 老师讲 ${this.subject} 课程`)
};
let t = new Teacher('马宾', 18, 'JS', '珠峰');
new 执行构造函数:
- 新开辟一个作用域
- 形参赋值
- 变量提升
- 隐式创建一个当前类的实例对象,并且把构造函数中的 this 指向当前实例
- 执行构造函数中的代码
- 隐式返回实例对象,相当于 return this
- 销毁作用域
console.log(t.name);
console.log(t.age);
t.teach(); // 调用 Teacher 的公有方法
3. 作为一个普通对象(所有引用数据类型的都是 Object 的一个实例);
function fe(a, b) {
console.log('I am an excellent FE cultivated by ZhuFeng');
}
fe(1, 2);
console.dir(fe); // {name: 'fe', length: 形参个数...}
console.log(fe.length); // 2 形参个数
console.log(fe.name); // 函数名
- 把函数当做普通对象使用,就像操作普通对象一样操作对象;通过这样的方式添加给函数(普通函数、构造函数)的属性或者方法称为静态属性或方法。
fe.age = 10;
fe.title = 'hello';
fe.greeting = function () {
console.log('hello world')
};
console.log(fe.age);
console.log(fe.title);
let f1 = new fe();
console.log(f1.age); // undefined
console.log(f1.title); // undefined
console.log(f1);
console.log(fe.prototype);
console.dir(fe);
- 注意:通过 函数名.xxx = xxx 添加的属性都是这个函数的私有属性。如果这个函数被当做构造函数使用(用 new 调用)时,这些属性既不是实例的属性也不是实例的公有属性,只能通过 函数名.xxx 的方式获取;
Array.isArray()
是数组 Array 的静态方法,只能通过 Array 自己调用;
Array.isArray() 检测一个值是否是一个数组,如果是返回 true,不是就返回 false
console.log(Array.isArray([])); // true
console.log(Array.isArray(1)); // false
三、call、apply、bind
this 是 js 代码执行时的环境对象,一般在函数中使用,在函数执行时,根据函数的调用方式不同而不同,在运行时不能通过赋值的方式修改;
1. this 的常见情况
- 事件中的 this 是绑定当前事件的元素;
- 自执行函数中的 this 指向 window;
- 定时器回调函数中的 this 指向 window;
- 全局作用域的 this 指向 window
- 方法调用时看方法执行前没有有点,如果有点前面是谁 this 就是谁,没有就是 window
- 箭头函数中的 this 是箭头函数声明时所在作用域中的 this
- 构造函数中的 this 指向当前实例
- Function.prototype 上的三个方法 call、apply、bind 供 Function 的实例用来修改函数中的 this 指向
console.log(Function.prototype);
1. call Function.prototype.call
function fe(a, b) {
console.log(a, b);
console.log(this);
}
fe(1, 2); // this -> window
- 使用 call 方法修改 this
- 语法:函数名.call(ctx, 实参1, 实参2…..)
- 参数:ctx 将函数中的 this 修改为 ctx; 从第二个参数开始,后面的参数都是传递给函数执行的实参
- 作用: 修改方法中的关键字为 call 方法的第一个实参 ctx,并且把后面的参数当做实参传给函数,最后让函数执行;
var obj = {
id: '0511120117'
};
fe.call(obj, 2, 5);
- 特殊情况:
fe.call(null, 1, 2);
fe.call(undefined, 1, 2);
fe.call();
// call的第一个参数传递 null、undefined、或者不传时函数的 this 是 window
2. apply 修改函数中的this关键字
- 语法:函数名.apply(ctx, [实参1, 实参2….])
- 参数:ctx 将函数中的this修改为ctx;第二个参数是一个数组,数组项都是传递给函数的实参;
- 作用:修改函数中的this关键字,并且把接收一个由实参组成的数组,最后把这个数组项作为实参传给函数,并且让函数执行
fe.apply(obj, [12, 13]); // 虽然这里传递了一个数组给函数,但是函数接收到的仍然是一个一个的实参
- call 和 apply 的区别:二者都是用来修改函数中的 this 关键字的;但是二者最后给函数传递实参的方式不同,call 是一个一个的传递,apply 是把实参放到一个数组中打包传递给函数。
bind 修改函数中的 this 关键字(绑定 this 关键字):
Function.prototype.bind
- 语法:函数.bind(ctx, 实参1, 实参2….)
- 作用:绑定函数中的 this 关键字,并且返回一个绑定了 this 的新函数;注意:bind 不会让函数执行
function f(a, b, c) {
console.log(a, b, c);
console.log('ff', this);
return a + b + c;
}
let obj2 = {
college: 'x'
};
let f2 = f.bind(obj2);
console.log(f2 === f); // false
f2(1, 2, 3);
f2(1, 3, 5);
console.log(f2);
bind 还有一个作用:绑定函数的参数
let f3 = f.bind(obj2, 10, 20);
f3(13); // 10 20 13
f3(14); // 10 20 14
let f4 = f.bind(obj2, 12); // x = 12
let f5= f4.bind(obj2, 13); // x = 12 y = 13
let f6 = f5.bind(obj2, 14); // x = 12 y = 13 z = 14
f6(); // 12 13 14
bind 方法实现函数柯里化
function sum(a, b, c) {
return a + b + c;
}
function curingSum(a) {
return function (b) {
return function (c) {
return a + b + c;
}
}
}
curingSum(1)(2)(3);
let c1 = sum.bind(null, 1);
let c2 = c1.bind(null, 2);
let c3 = c2.bind(null, 3);
let r = c3();
console.log(r);