一、Function类:函数类

  • 所有的函数(类、普通函数)都是 Function 的一个实例

内置类:

  1. // Array Number String Boolean Object Date Function 等
  2. console.log(typeof Array); // function
  3. console.log(typeof Number); // function
  • 内置类和 Function 类

内置类都是 Function 类实例,而实例都有一个 proto**** 指向所属类的原型对象
js 中所有的函数都是 Function 的实例,那么内置类,如 Array 是函数,所以 Array 也是 Function 的实例;

  1. console.log(Array instanceof Function);

既然是实例,那么一定也会有原型关系

  1. console.dir(Array); // 通过打印发现 Array 也是一个对象,它也有 __proto__,根据原型关系,Array 的 __proto__ 应该指向的是 Function 的 prototype
  2. Array.__proto__ === Function.prototype; true
  3. console.log(Date instanceof Function); true
  4. console.log(Date.__proto__ === Function.prototype); true
  5. console.log(RegExp instanceof Function); true
  6. console.log(RegExp.__proto__ === Function.prototype); true
  7. console.log(Object instanceof Function); true
  8. console.log(Object.__proto__ === Function.prototype); true
  9. 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 的关系

  1. Object.proto 指向 Function.prototype
  1. console.log(Object.__proto__ === Function.prototype);
  1. Function.prototype.proto 指向 Object.prototype
  1. console.log(Function.prototype.__proto__ === Object.prototype);
  1. 所有的函数都是 Function 的实例
  1. console.log(Array instanceof Function);
  1. 所有的引用数类型(普通对象、实例对象、函数、类、数组、Date)的都是 Object 这个基类的实例,所以函数也是对象;
  1. console.log(Function instanceof Object);
  2. console.log(Array instanceof Object);
  3. console.log(Date instanceof Object);
  4. let obj = {
  5. id: 1
  6. };
  7. console.log(obj instanceof Object);
  8. function fn() {
  9. console.log('fn')
  10. }
  11. fn.name = '珠峰';
  12. fn.age = 10;
  13. console.log(fn.age);
  14. console.log(fn.age);

总结

  • 所有的函数数据类型都是 Function 的实例
  1. function Fn() {}
  2. console.log(Fn instanceof Function); // true
  • Function 函数类 本身也是一个函数
  1. console.log(typeof Function); // function
  2. console.log(Function instanceof Function); // true 所以 Function 也是自己的的一个实例
  • 因为 Function 是自己的实例,所以 Function.proto 指向自己的 prototype
  1. console.log(Function.__proto__ === Function.prototype);
  • Function 也是 Object 基类的实例:所以函数也是对象,可以有自己的私有属性
  1. console.log(Function instanceof Object); // true
  • js 的内置引用类型都是 Function 的实例
  1. console.log(Object instanceof Function);
  2. console.log(Object.__proto__ === Function.prototype)

二、函数的三种角色

函数的三种角色:

  1. 作为一个普通函数执行(形参、实参、返回值)
  2. 作为一个类(new Fn 构造函数执行)
  3. 函数也是一个普通对象(通过 .属性名 或者 [‘属性名’] 获取私有属性);

1. 普通函数

  1. function sum(a, b) {
  2. var x = 1;
  3. var y = 12;
  4. var z = 123;
  5. return a + b + x + y + z;
  6. }
  7. var result = sum(1, 3);
  8. console.log(result);

普通函数的执行过程:

  1. 开辟一个新的作用域
  2. 形参赋值
  3. 变量提升
  4. 函数体从上到下执行
  5. 销毁作用域

2. 构造函数(类):

  • 2.1 每个构造函数都有一个 prototype 属性,它的值是一个对象,用来存放当前类型的公有的属性和方法
  • 2.2 必须通过 new 操作符调用函数才能返回一个实例对象;
  1. function Teacher(n, a, s, f) {
  2. // 通过 this.xxx = xxx 给实例对象添加私有属性
  3. this.name = n;
  4. this.age = a;
  5. this.subject = s;
  6. this.from = f;
  7. }
  • 在原型上增加的方法都是这个类型公有的属性和方法
  1. Teacher.prototype.teach = function () {
  2. console.log(`${this.name} 老师讲 ${this.subject} 课程`)
  3. };
  4. let t = new Teacher('马宾', 18, 'JS', '珠峰');

new 执行构造函数:

  1. 新开辟一个作用域
  2. 形参赋值
  3. 变量提升
  4. 隐式创建一个当前类的实例对象,并且把构造函数中的 this 指向当前实例
  5. 执行构造函数中的代码
  6. 隐式返回实例对象,相当于 return this
  7. 销毁作用域
  1. console.log(t.name);
  2. console.log(t.age);
  3. t.teach(); // 调用 Teacher 的公有方法

3. 作为一个普通对象(所有引用数据类型的都是 Object 的一个实例);

  1. function fe(a, b) {
  2. console.log('I am an excellent FE cultivated by ZhuFeng');
  3. }
  4. fe(1, 2);
  5. console.dir(fe); // {name: 'fe', length: 形参个数...}
  6. console.log(fe.length); // 2 形参个数
  7. console.log(fe.name); // 函数名
  • 把函数当做普通对象使用,就像操作普通对象一样操作对象;通过这样的方式添加给函数(普通函数、构造函数)的属性或者方法称为静态属性或方法。
  1. fe.age = 10;
  2. fe.title = 'hello';
  3. fe.greeting = function () {
  4. console.log('hello world')
  5. };
  6. console.log(fe.age);
  7. console.log(fe.title);
  8. let f1 = new fe();
  9. console.log(f1.age); // undefined
  10. console.log(f1.title); // undefined
  11. console.log(f1);
  12. console.log(fe.prototype);
  13. console.dir(fe);
  • 注意:通过 函数名.xxx = xxx 添加的属性都是这个函数的私有属性。如果这个函数被当做构造函数使用(用 new 调用)时,这些属性既不是实例的属性也不是实例的公有属性,只能通过 函数名.xxx 的方式获取;

Array.isArray()

是数组 Array 的静态方法,只能通过 Array 自己调用;

Array.isArray() 检测一个值是否是一个数组,如果是返回 true,不是就返回 false

  1. console.log(Array.isArray([])); // true
  2. console.log(Array.isArray(1)); // false

三、call、apply、bind

this 是 js 代码执行时的环境对象,一般在函数中使用,在函数执行时,根据函数的调用方式不同而不同,在运行时不能通过赋值的方式修改;

1. this 的常见情况

  1. 事件中的 this 是绑定当前事件的元素;
  2. 自执行函数中的 this 指向 window;
  3. 定时器回调函数中的 this 指向 window;
  4. 全局作用域的 this 指向 window
  5. 方法调用时看方法执行前没有有点,如果有点前面是谁 this 就是谁,没有就是 window
  6. 箭头函数中的 this 是箭头函数声明时所在作用域中的 this
  7. 构造函数中的 this 指向当前实例
  • Function.prototype 上的三个方法 call、apply、bind 供 Function 的实例用来修改函数中的 this 指向
  1. console.log(Function.prototype);

1. call Function.prototype.call

  1. function fe(a, b) {
  2. console.log(a, b);
  3. console.log(this);
  4. }
  5. fe(1, 2); // this -> window
  • 使用 call 方法修改 this
  • 语法:函数名.call(ctx, 实参1, 实参2…..)
  • 参数:ctx 将函数中的 this 修改为 ctx; 从第二个参数开始,后面的参数都是传递给函数执行的实参
  • 作用: 修改方法中的关键字为 call 方法的第一个实参 ctx,并且把后面的参数当做实参传给函数,最后让函数执行;
  1. var obj = {
  2. id: '0511120117'
  3. };
  4. fe.call(obj, 2, 5);
  • 特殊情况:
  1. fe.call(null, 1, 2);
  2. fe.call(undefined, 1, 2);
  3. fe.call();
  4. // call的第一个参数传递 null、undefined、或者不传时函数的 this 是 window

2. apply 修改函数中的this关键字

  • 语法:函数名.apply(ctx, [实参1, 实参2….])
  • 参数:ctx 将函数中的this修改为ctx;第二个参数是一个数组,数组项都是传递给函数的实参;
  • 作用:修改函数中的this关键字,并且把接收一个由实参组成的数组,最后把这个数组项作为实参传给函数,并且让函数执行
  1. fe.apply(obj, [12, 13]); // 虽然这里传递了一个数组给函数,但是函数接收到的仍然是一个一个的实参
  • call 和 apply 的区别:二者都是用来修改函数中的 this 关键字的;但是二者最后给函数传递实参的方式不同,call 是一个一个的传递,apply 是把实参放到一个数组中打包传递给函数。

bind 修改函数中的 this 关键字(绑定 this 关键字):

Function.prototype.bind

  • 语法:函数.bind(ctx, 实参1, 实参2….)
  • 作用:绑定函数中的 this 关键字,并且返回一个绑定了 this 的新函数;注意:bind 不会让函数执行
  1. function f(a, b, c) {
  2. console.log(a, b, c);
  3. console.log('ff', this);
  4. return a + b + c;
  5. }
  6. let obj2 = {
  7. college: 'x'
  8. };
  9. let f2 = f.bind(obj2);
  10. console.log(f2 === f); // false
  11. f2(1, 2, 3);
  12. f2(1, 3, 5);
  13. console.log(f2);

bind 还有一个作用:绑定函数的参数

  1. let f3 = f.bind(obj2, 10, 20);
  2. f3(13); // 10 20 13
  3. f3(14); // 10 20 14
  4. let f4 = f.bind(obj2, 12); // x = 12
  5. let f5= f4.bind(obj2, 13); // x = 12 y = 13
  6. let f6 = f5.bind(obj2, 14); // x = 12 y = 13 z = 14
  7. f6(); // 12 13 14

bind 方法实现函数柯里化

  1. function sum(a, b, c) {
  2. return a + b + c;
  3. }
  4. function curingSum(a) {
  5. return function (b) {
  6. return function (c) {
  7. return a + b + c;
  8. }
  9. }
  10. }
  11. curingSum(1)(2)(3);
  12. let c1 = sum.bind(null, 1);
  13. let c2 = c1.bind(null, 2);
  14. let c3 = c2.bind(null, 3);
  15. let r = c3();
  16. console.log(r);