在 js 中 无论是函数, 还是方法, 还是事件, 还是构造器, ... 其本质都是函数. 只是处在不同的位子而已。
四种模式有:函数模式,方法模式,构造器模式,上下文模式
1.函数模式
特征: 就是一个简单的函数调用. 函数名的前面没有任何引导内容,其中this表示全局对象,在浏览器中是window。 比如:
function foo () {}
var func = function () {};
...
foo();
func();
(function (){})();
2.方法模式
特征: 方法一定是依附于一个对象, 将函数赋值给对象的一个属性, 那么就成为了方法,其中this指的是这个依附的对象.
比如:
function f() {
this.method = function () {};}var o = { method: function () {} }
3.构造器调用模式 特征:
1. 使用 new 关键字, 来引导构造函数.2. 构造函数中发 this 与方法中一样, 表示对象, 但是构造函数中的对象是刚刚创建出来的对象3. 构造函数中不需要 return, 就会默认的 return this 补充:1. 如果手动的添加 return, 就相当于 return this2. 如果手动的添加 return 基本类型; 无效, 还是保留原来 返回 this3. 如果手动添加 return null; 或 return undefiend, 无效4. 如果手动添加 return 对象类型; 那么原来创建的 this 就会被丢掉, 返回的是 return 后面的对象
由于构造函数只是给 this 添加成员. 没有做其他事情. 而方法也可以完成这个操作, 就 this 而言,构造函数与方法没有本质区别.
1. 工厂方法 // 工厂就是用来生产的, 因此如果函数创建对象并返回, 就称该函数为工厂函数 function createPerson( name, age, gender ) { var o = {}; o.name = name; o.age = age; o.gender = gender; return o; } // document.createElement() 2. 构造方法3. 寄生式创建对象 // 外表看起来就是构造方法, 但是本质不是的构造方法创建对象的方式 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‘ ); 4. 混合式创建
一个案例:<!DOCTYPE h<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
</body>
<script>
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(); // 1 一开始赋值全局的 getName 为 1, 然后返回 this
getName(); // 1
new Foo.getName(); // 2
new Foo().getName(); // 3
new new Foo().getName(); // 3
</script>
</html>
4.上下文调用模式
上下文就是环境,也就是自定义设置this的含义.
语法: 1. 函数名.apply( 对象, [ 参数 ] ); 2. 函数名.call( 对象, 参数 ); 描述: 1. 函数名就是表示的函数本身, 使用函数进行调用的时候默认 this 是全局变量 2. 函数名也可以是方法提供, 使用方法调用的时候, this 是指当前对象. 3. 使用 apply 进行调用后, 无论是函数, 还是方法都无效了. 我们的 this, 由 apply 的第一个参数决定 注意: 1. 如果函数或方法中没有 this 的操作, 那么无论什么调用其实都一样. 2. 如果是函数调用 foo(), 那么有点像 foo.apply( window ). 3. 如果是方法调用 o.method(), 那么有点像 o.method.apply( o ). 参数问题 无论是 call 还是 apply 在没有后面的参数的情况下( 函数无参数, 方法无参数 ) 是完全一样的. function foo() { console.log( this ); } foo.apply( obj ); foo.call( obj ); 第一个参数的使用也是有规则的 1. 如果传入的是一个对象, 那么就相当于设置该函数中的 this 为参数 2. 如果不传入参数, 或传入 null. undefiend 等, 那么相当于 this 默认为 window foo(); foo.apply(); foo.apply( null ); foo.call( undefined ); 3. 如果传入的是基本类型, 那么 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 ) 处理数组转换, 实际上就是将元素一个一个的取出来构成一个新数组, 凡是涉及到该操作的方法理论上都可以 1. push, unshift 2. slice 3. 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 ] ); 求数组中的最大值 传统 var max = arr[ 0 ]; for ( var i = 1; i < arr.length; i++ ) { if ( arr[ i ] > max ) { ... } } 还是利用 apply 可以展开数组的特性 var arr = [ 123456,12345,1234,345345,234,5 ]; Math.max.apply( null, arr ); 借用构造函数继承 function Person ( name, age, gender ) { this.name = name; this.age = age; this.gender = gender; } // 需要提供一个 Student 的构造函数创建学生对象 // 学生也应该有 name, age, gender, 同时还需要有 course 课程 function Student ( name, age, gender, course ) { Person.call( this, name, age, gender ); this.course = course; }