一、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);