【转】函数的四种调用方式
函数的四种调用模式
在 js 中 无论是函数, 还是方法, 还是事件, 还是构造器, ... 其本质都是函数. 只是处在不同的位子而已.
四种:
- 函数模式
- 方法模式
- 构造器模式
- 上下文模式
函数模式
特征: 就是一个简单的函数调用. 函数名的前面没有任何引导内容.
function foo () {}
var func = function () {};
...
foo();
func();
(function (){})();
this 的含义: 在 函数中 this 表示全局对象, 在浏览器中是 window
方法模式
特征: 方法一定是依附于一个对象, 将函数赋值给对象的一个属性, 那么就成为了方法.
function f() {
this.method = function () {};
}
var o = {
method: function () {}
}
this 的含义: 这个依附的对象.
构造器调用模式
创建对象的时候 构造函数做了什么?
由于构造函数只是给 this 添加成员. 没有做其他事情. 而方法也可以完成这个操作, 就 this 而言,
构造函数与方法没有本质区别.
特征:
- 使用 new 关键字, 来引导构造函数.
- 构造函数中发 this 与方法中一样, 表示对象, 但是构造函数中的对象是刚刚创建出来的对象
- 构造函数中不需要 return, 就会默认的 return this
补充:
- 如果手动的添加 return, 就相当于 return this
- 如果手动的添加 return 基本类型; 无效, 还是保留原来 返回 this
- 如果手动添加 return null; 或 return undefiend, 无效
- 如果手动添加 return 对象类型; 那么原来创建的 this 就会被丢掉, 返回的是 return 后面的对象
创建对象的模式
-
工厂方法
// 工厂就是用来生产的, 因此如果函数创建对象并返回, 就称该函数为工厂函数 function createPerson( name, age, gender ) { var o = {}; o.name = name; o.age = age; o.gender = gender; return o; } // document.createElement() - 构造方法
-
寄生式创建对象
// 外表看起来就是构造方法, 但是本质不是的构造方法创建对象的方式 function createPerson( name, age, gender ) { var o = {}; o.name = name; o.age = age; o.gender = gender; return o; } var p = new createPerson( 'jim', 19, 'male' ); -
混合式创建
上下文调用模型
上下文 就是环境, 就是自定义设置 this 的含义
语法:
- 函数名.apply( 对象, [ 参数 ] );
- 函数名.call( 对象, 参数 );
描述:
- 函数名就是表示的函数本身, 使用函数进行调用的时候默认 this 是全局变量
- 函数名也可以是方法提供, 使用方法调用的时候, this 是指当前对象.
- 使用 apply 进行调用后, 无论是函数, 还是方法都无效了. 我们的 this, 由 apply 的第一个参数决定
注意:
- 如果函数或方法中没有 this 的操作, 那么无论什么调用其实都一样.
- 如果是函数调用 foo(), 那么有点像 foo.apply( window ).
- 如果是方法调用 o.method(), 那么有点像 o.method.apply( o ).
参数问题
无论是 call 还是 apply 在没有后面的参数的情况下( 函数无参数, 方法无参数 ) 是完全一样的.
function foo() {
console.log( this );
}
foo.apply( obj );
foo.call( obj );
第一个参数的使用也是有规则的
- 如果传入的是一个对象, 那么就相当于设置该函数中的 this 为参数
-
如果不传入参数, 或传入 null. undefiend 等, 那么相当于 this 默认为 window
foo(); foo.apply(); foo.apply( null ); foo.call( undefined ); - 如果传入的是基本类型, 那么 this 就是基本类型对应的包装类型的引用
- number -> Number
- boolean -> Boolean
- string -> String
除了 this 的参数外的参数
在使用 上下文调用的 时候, 原函数(方法)可能会带有参数, 那么这个参数在上下文调用中使用 第二个( 第 n 个 )参数来表示
function foo( num ) {
console.log( num );
}
foo.apply( null, [ 123 ] );
// 等价于
foo( 123 );
应用
上下文调用只是能修改 this, 但是使用的最多的地方上是借用函数调用.
将伪数组转换为数组
传统的做法
var a = {};
a[ 0 ] = 'a';
a[ 1 ] = 'b';
a.length = 2;
// 数组自带的方法 concat
// 语法: arr.concat( 1, 2, 3, [ 4, [ 5 ] ] );
// 特点不修改原数组
var arr = [];
var newArr = arr.concat( a );
由于 a 是伪数组, 只是长得像数组. 所以这里不行, 但是 apply 方法有一个特性, 可以将数组或伪数组作为参数
foo.apply( obj, 伪数组 ); // IE8 不支持
将 a 作为 apply 的第二个参数
var newArr = Array.prototype.concat.apply( [], a )
处理数组转换, 实际上就是将元素一个一个的取出来构成一个新数组, 凡是涉及到该操作的方法理论上都可以
- push, unshift
- slice
- splice
push 方法
用法: arr.push( 1 ); 将这个元素加到数组中, 并返回所加元素的个数
arr.push( 1, 2, 3 ); 将这三个元素依次加到数组中, 返回所加个数
var a = { length: 0 }; // 伪数组
a[ a.length++ ] = 'abc'; // a[ 0 ] = 'abc'; a.length++;
a[ a.length++ ] = 'def';
// 使用一个空数组, 将元素一个个放到数组中即可
var arr = [];
arr.push( a ); // 此时不会将元素展开, 而是将这个伪数组作为一个元素加到数组中
// 再次利用 apply 可以展开伪数组的特征
arr.push.apply( arr, a );
// 利用 apply 可以展开伪数组的特性, 这里就相当于 arr.push( a[0], a[1] )
slice
语法: arr.slice( index, endIndex )
如果第二个参数不传, 那么就是 从 index 一致获取到结尾
该方法不会修改原数组
var a = { length: 0 };
a[ a.length++ ] = 'abc';
a[ a.length++ ] = 'def';
// 假设他是一个数组, 就是应该 从 0 项开始截取到 最后
// 如果可以的话, 应该 a.slice( 0 )
// 但是他没有该方法
// 借用 数组的 slice, 将 this 转换成 这个伪数组
var arr = [];
var newArr = arr.slice.apply( a, [ 0 ] );
dijkstra模板(好像是斐波那契额堆优化,但我为什么看起来像优先队列优化,和spfa一样)
最大空凸包模板
ICPC 2017–2018, NEERC, Northern Subregional Contest St Petersburg, November 4, 2017 I题
hdu 5248 序列变换
hdu 2063(二分图模板测试)
组合数
85. Maximal Rectangle 由1拼出的最大矩形
750. Number Of Corner Rectangles四周是点的矩形个数
801. Minimum Swaps To Make Sequences Increasing 为使两个数组严格递增,所需要的最小交换次数