函数的四种调用模式

概念

  1. 在 js 中,无论是函数, 还是方法, 还是事件, 还是构造器,...这些东西的本质都是函数
  2. 函数, 方法, 事件, 构造器,...只是所处的位置不同
  3. 这四种模式分别是
    • 函数模式
    • 方法模式
    • 构造器模式
    • 上下文模式

函数模式

特征: 简单的函数调用, 函数名前面没有任何引导内容

    function foo(){}
var fn = function(){};
...
foo();
fn();
(function(){})();
// 上面的三种都是简单的函数调用

this的含义

  1. 在函数中 this 表示全局对象
  2. 在浏览器中 this 表示 window(js 中的 global)

方法模式

特征: 方法一定是依附于一个对象, 将函数赋值给对象的一个属性, 那么就成为方法.就是函数前面必须有引导对象

    function foo(){
this.method = function(){};
}
var o = {
method : function(){}
}

this的含义

  • 这个依附的对象(引导函数的对象)

注意点

在 arguments 这种伪数组, 或者 [] 数组这样的对象中, 这样调用函数也是方法调用, this 会只指向对象

构造器模式

构造函数在创建对象的时候, 做了些什么

  1. 使用 new 引导构造函数, 创建了一个实例对象
  2. 在创建对象的同时, 将this指向这个刚刚创建的对象
  3. 在构造函数中, 不需要 return , 会默认的 return this

分析:

由于构造函数只是给 this 添加成员, 而方法也可以完成这个操作,对与 this 来说, 构造函数和方法没有本质区别

关于return的补充, 在构造函数中

普通情况, 可以理解为构造函数已经默认进行了 return this, 添加在后面的都不会执行

  1. 如果手动的添加 return ,就相当于 return this.
  2. 如果手动的添加 return 基本类型(字符串, 数字, 布尔), 无效, 还是 return this
  3. 如果手动的添加 return null 或 return undefined, 无效, 还是 return this

特殊情况, return 对象, 最终返回对象

  • 手动添加 return 对象类型, 那么原来创建的 this 会被丢掉, 返回 return 后面的对象

上下文模式

概念: 上下文就是环境, 就是自定义this的含义

语法:

  1. 函数名.apply( 对象, [参数]);

    • 这个参数可以是数组, 也可以是伪数组
  2. 函数名.call( 对象, 参数);
    • 多个参数可以通过,进行隔离

描述:

  1. 函数名表示的是函数本身, 使用函数进行调用的时候,默认this指的是全局变量
  2. 函数名也可以是方法提供, 使用方法调用的时候, this指的是当前对象
  3. 使用 apply 或者 call 进行调用后, 无论是函数, 还是方法的 this 指向全部无效了, this 的指向由 apply 或者 call 的第一个参数决定

注意:

  1. 如果函数或方法中没有this的操作, 那么无论是哪一种函数调用模式, 其实都一样
  2. 如果是函数调用 foo(), 其实和 foo.apply(window) 类似
  3. 如果是方法调用 o.method(), 其实和 o.method.apply(o)

无论是 call 还是 apply 在没有后面参数的情况下(函数无参数, 方法无参数), 两者一致

    function foo(){
console.log(this); // this => window
}
var obj = {};
foo.apply( obj ); // this => obj
foo.call( obj ); // this => obj

apply 和 call 第一个参数的使用规则

  1. 如果传入的是一个对象, 就相当于设置该函数中的this为参数
  2. 如果不传参数, 或者传入 null, undefined 等,那么this就默认是 window
    foo();
foo.apply();
foo.apply(null);
foo.apply(undefined);
foo.call();
foo.call(null);
foo.call(undefined);
// 上面都this都指向window
  1. 如果传入的是基本类型, 那么this指向的就是基本类型的包装类型的引用

    • number => Number
    • boolean => Boolean
    • string => String

除 this 外的其他参数

再使用上下文调用的时候, 原函数(方法)可能会带有参数, 那么要让这些参数在上下文中调用, 就需要这个第二, ( n )个参数来表示

    function foo(num){
console.log(num);
}
foo.apply(null, [123]);
// 相当于
foo(123);

应用

上下文调用只是修改this, 但是使用最多的地方是借用函数调用

  1. 将伪数组转换为数组

    • 传统方法
        var a = {};
    a[0] = 'a';
    a[1] = 'b';
    a.length = 2;
    // 使用数组自带方法 concat();
    // 不修改原数组
    var arr = [];
    var newArr = arr.concat(a);

    分析

    由于 a 是伪数组, 并不是真正的数组, 不能使用数组的方法, concat 会将 a 作为一个整体 Object 加入数组

    apply 方法有一个特性, 可以将数组或者伪数组作为参数

        foo.apply( obj, 伪数组 ); // IE8 不支持

    将 a 作为 apply 的第二个参数

        var arr = [];
    var newArr = Array.prototype.concat.apply( arr, a);

    由上面的数组转换, 我们可以得到结论, 应该涉及到数组操作的方法理论上都应该可以

    push, pop, unshift, shift

    slice

    splice

  2. 让伪数组使用 push 方法

    小技巧, 伪数组添加元素
        var a = {length : 0};   // 设置伪数组的长度
    a[ a.length++ ] = 'a';
    a[ a.length++ ] = 'b';
    // 在给伪数组的元素赋值时, 同时修改伪数组的 length 属性
    // a[0] = 'a'; a.length++;

    伪数组使用 push 方法

        var arr = [];
    arr.push.apply( arr, a );
    // 利用 apply 可以处理伪数组的特性, 进行传参
    // 相当于 arr.push(a[0], a[1])
  3. 让伪数组使用 slice 方法

    数组的 slice 语法

    • arr.slice( index, endIndex ), 不包含 endIndex
    • 如果第二个参数不传参, 那么截取从 index 一直到数组结尾
    • slice 方法不会修改原数组

    通过 apply 实现伪数组使用 slice 方法

        var a = { length : 0 };
    a[a.length++] = 'a';
    a[a.length++] = 'b';
    var arr =[];
    var newArr = arr.slice.apply(a ,[0])

获取数组中的最大值

传统方法

    var arr = [1,2,3,4,5,6,7,8,9];
var max = arr[0];
for(var i = 0;i < arr.length;i++){
if(max < arr[i]){
max = arr[i]
}
}

使用 apply 借用 Math.max 获取数组中最大值

利用 apply 可以传入参数可以是数组或是伪数组的特性

    var arr = [1,2,3,4,5,6,7,8,9];
Math.max.apply(null, arr);

创建对象的几种模式

了解了四种函数调用模式, 我们可以深一步了解创建对象的几种方式, 对象是通过构造函数创建的

  1. 工厂模式

    特点:

    1. 大量重复执行的代码, 解决重复实例化的问题
    2. 函数创建对象并返回
    3. 最典型的工厂模式就是 document.createElement()
    4. 无法知道是谁创建了这个实例对象
        function createPerson(name, age, gender){
    var o = {};
    o.name = name;
    o.age = age;
    o.gender = gender;
    return o;
    }
  2. 构造方法

    特点:

    1. 解决了重复实例化问题
    2. 能够知道是谁创建了这个对象(constructor)
    3. 需要通过 new 运算符穿件对象
        function Person(name,age,gender){
    this.name = name;
    this.age = age;
    this.gender = gender;
    }
  3. 寄生式构造函数创建对象

    特点:

    1. 外表看起来就是构造犯法, 但本质不是通过构造方法创建对象
    2. 工厂模式 + 构造函数模式
    3. 不能确定对象的关系, 不推荐使用
        function createPerson(name,age,gender){
    var o = {};
    o.name = name;
    o.age = age;
    o.gender = gender;
    return o;
    }
    var p = new createPerson('Bob',19,'male')
  4. 混合式创建
    1. 构造函数 + 原型
    2. 解决了构造函数传参和共享的问题
    3. 不共享的参数使用构造函数
    4. 共享的使用原型
    5. 这种混合模式很好的解决了传参引用共享的难题
        function createPerson(name,age,gender){
    this.name = name;
    this.age = age;
    this.gender = gender;
    }
    createPerson.prototype = {
    constructor : createPerson,
    wife : '高圆圆'
    }
  5. 借用构造函数继承(对象冒充)

    特点:

    1. 借用构造函数(对象冒充)只能继承构造函数的成员, 无法继承原型的成员
        function Person(name,age,gender){
    this.name = name;
    this.age = age;
    this.gender = gender;
    }
    function Studner(name,age,gender,course){
    // 借用构造函数Person, 创建 Student 对象
    Person.call(this,name,age,gender);
    this.course = course;
    }
    var boy = new Student('Bob',19,'male',;Math);
  6. 寄生式继承

    特点:

    1. 原型 + 工厂模式
    2. 通过临时中转
        // 临时中转
    function person(o){
    function Foo(){};
    F.prototype = o;
    return new F();
    }
    // 寄生函数
    function create(o){
    var fn = person(o);
    fn.move = function(){};
    fn.eat = function(){};
    fn.sleep = function(){};
    return fn;
    }
    var boy = {
    name : 'Bob',
    age : 19,
    famliy : ['father','mother','wife']
    }
    var boy1 = create(boy);
    console.log(boy1.name);
    console.log(boy1.family);
    // 此时 boy1 有了 boy 的成员

经典例题

例题 1

    function Foo(){
getName = function(){ alert(1); };
return this;
}
function getName(){
alert(5);
}
Foo.getName = function(){ alert(2); };
Foo.prototype.getName = function(){ alert(3); }; getName = function(){ alert(4); }; Foo.getName(); // 2
getName(); // 4
Foo().getName(); // 4 1 getName(); // 4 1
new Foo.getName(); // 2
new Foo().getName(); // 3 new new Foo().getName(); // 3

分析

  1. 预解析
  • 函数名 Foo 和函数名 getName 声明提升,函数名和函数体绑定
  1. 执行代码
  • 执行 Foo.getName();

    • 输出 2
  • 执行 getName();
    • 此时 getName 是在全局中, 未被修改, 输出4
  • Foo().getName();
    • 此事 Foo() 只是一个函数, 执行完成后 getName 被重新赋值
    • getName 因为被重新赋值为 1, 输出1
  • getName()
    • 由于 getName 被重新赋值, 所以输出 1
  • new Foo.getName();
    • Foo.getName 并未被修改
    • new 没有起任何作用
    • 输出2
  • new Foo().getName();
    • 构造函数创建了实例对象
    • 对象中没有 getName 方法, 要从对象的构造函数中的原型中寻找
    • 在 Foo.prototype 中得到 getName 输出为 3
  • new new Foo().getName();
  • 构造函数创建了实例对象
  • 对象中没有 getName 方法, 要从对象的构造函数中的原型中寻找
  • new 没有起作用
  • 在 Foo.prototype 中得到 getName 输出为 3

例题 2

var length = 10;
function fn() {
console.log( this.length );
} var obj = {
length: 5,
method: function ( fn ) {
fn();
arguments[ 0 ](); // [ fn, 1, 2, 3, length: 4]
}
};
obj.method( fn, 1, 2, 3 );

分析

  1. 预解析

    • 变量名 length, obj 和 函数名fn 声明提升, 函数名和函数体绑定
  2. 执行代码
    • length = 10, 此时 length 可以看作是 window 下的属性
    • obj = {}, 进行赋值
    • 执行 obj 中的 method 方法
    • 将 函数体 fn 进行传参
    • 跳进函数 fn,执行函数 fn(), 函数中的 this 指的是 window 下的 length, 为10
    • 明确argument是一个对象, argument[0] 指的是 fn
    • 使用对象调用函数, 这里的 this 指的是 argument 这个对象
    • 得到 argument.length 为 4
    • 最后输出结果为 10 4

例题 3

    var o = {
method : function(){
console.log(this);
}
};
o.method();
var f = o.method;
f();
(o.method)();
var obj = {};
(obj.fn = o.method)();
(obj.fn)();

分析

  1. 预解析

    • 变量名 o, f ,obj 声明提升
  2. 执行函数
    • o = {},进行赋值
    • 执行o.method方法, 这里的 this 指的是 o 这个对象
    • 将 o.method 函数体,赋值给f
    • 执行 f(), 这里的 this 指的是window
    • (o.method)是一个函数表达式, 和 o.method() 的结果一致
    • obj = {}, obj进行赋值
    • o.method 的函数体, 赋值给obj.fn, 执行之后, 这里的 this 指的是window
    • (obj.fn)是一个函数表达式, 和 obj.fn() 的结果一致, tshi 指向 obj

JS面向对象函数的四种调用模式的更多相关文章

  1. js高级-函数的四种调用模式

    1.对象方法调用模式  方法内部的this指向当前调用者的对象d 定义类 (构造函数) function Dog (dogName){ //创建一个空对象   让空对象==this this.name ...

  2. 函数的四种调用模式.上下文调用.call.apply

    闭包:函数就是一个闭包,一个封闭的作用域;         返回函数,要返回多个函数就用一个对象封装一下,         立即执行函数+return 回调函数   JS动态创建的DOM,不会被搜索引 ...

  3. js中this的四种调用模式

    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/ ...

  4. javascript中函数的四种调用模式详解

    介绍函数四种调用模式前,我们先来了解一下函数和方法的概念,其实函数和方法本质是一样,就是称呼不一样而已.函数:如果一个函数与任何对象关系,就称该函数为函数.方法:如果一个函数作为一个对象属性存在,我们 ...

  5. JS函数的四种调用模式

    函数在js中具有四种身份,分别为函数.方法.构造函数.apply或call调用 函数调用    函数调用模式中this指全局对象(window) var f1 = function() { alert ...

  6. javascript函数的四种调用模式及其this关键字的区别

    方法调用模式: 当一个函数被保存为对象的一个属性时,我们称它为一个方法.当一个方法被调用时,this被绑定到该对象. //方法调用模式 var myObject = { value: 0 , incr ...

  7. JavaScript (JS) 函数补充 (含arguments、eval()、四种调用模式)

    1. 程序异常 ① try-catch语法    测试异常 try-catch语法代码如下: try { 异常代码;     try中可以承重异常代码, console.log(“try”)  出现异 ...

  8. JS高级. 06 缓存、分析解决递归斐波那契数列、jQuery缓存、沙箱、函数的四种调用方式、call和apply修改函数调用方法

    缓存 cache 作用就是将一些常用的数据存储起来 提升性能 cdn //-----------------分析解决递归斐波那契数列<script> //定义一个缓存数组,存储已经计算出来 ...

  9. JavaScript高级之函数的四种调用形式

    主要内容 分析函数的四种调用形式 弄清楚函数中this的意义 明确构造函对象的过程 学会使用上下文调用函数 了解函数的调用过程有助于深入学习与分析JavaScript代码. 本文是JavaScript ...

随机推荐

  1. hdu 6223 Infinite Fraction Path

    题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=6223 题意:给定长度为n的一串数字S,现在要按照一种规则寻找长度为n的数字串,使得该数字串的字典序最大 ...

  2. 【FJWC2017】交错和查询 [线段树]

    交错和查询 Time Limit: 10 Sec  Memory Limit: 256 MB Description 无限循环数字串S由长度为n的循环节s构成.设s为12345(n=5),则数字串S为 ...

  3. [目前未找到题目]扩展KMP模板

    procedure build_next; begin lena:=length(a);lenb:=length(b); next[]:=lenb;next[]:=lenb-; to lenb- ] ...

  4. ecma 2018, javascript spread syntax behaves like Object.assign

    as the subject. It is only supported in Chrome version 60+, so, first check the version, or just use ...

  5. DB 基本性能指标

    DB: •500K I/O limit with kill(5M I/O limit for DWS) •10,000 return row limit with kill •30 seconds p ...

  6. HDU 1162 Eddy's picture (最小生成树 普里姆 )

    题目链接 Problem Description Eddy begins to like painting pictures recently ,he is sure of himself to be ...

  7. Sublime Text 3 遇到的一些小坑的解决方法

    1.[不停弹出更新框]Sublime Text 3 软件会弹出“Update Available”对话框,点击“Cancel”按钮取消:取消之后还是会频繁出现 解决方法:点击菜单栏“Preferenc ...

  8. 【Mysql优化】索引碎片与维护

    在长期的数据更改过程中, 索引文件和数据文件,都将产生空洞,形成碎片.(不停的删除修改导致) 解决办法: (1)我们可以通过一个nop操作(不产生对数据实质影响的操作), 来修改表. 比如: 表的引擎 ...

  9. Python 模拟SQL对文件进行增删改查

    #!/usr/bin/env python # _*_ coding:UTF-8 _*_ # __auth__: Dalhhin # Python 3.5.2,Pycharm 2016.3.2 # 2 ...

  10. Unordered load/store queue

    A method and processor for providing full load/store queue functionality to an unordered load/store  ...