立即执行函数表达式(自执行函数)
自执行函数的格式
//1. 把整个函数体加小括号包在小括号里面
(function (){}(
alert(0);
));
//2. 只把函数体放在小括号里,调用的小括号放在外面
(function (){
alert(0);
}) ();
// 也可以像以下这样使用自执行函数
+function (){
alert(0)
}()
-function (){
alert(0)
}()
!function (){
alert(0)
}()
~function (){
alert(0)
}()
^function (){
alert(0)
}()
// 上面的语法都可以让自执行函数跑起来
沙箱模式:
所谓沙箱 就是模拟现实世界的!沙箱中发生任何改变不会对现实世界产生任何影响沙箱要实现隔离的效果,外界不会对沙箱产生影响,沙箱中也不会对外界有影响
js中的沙箱模式
(function(w){
//写所有函数和变量的声明
//写功能逻辑代码
//如果需要,就通过window对象想外界暴露接口
// 例如: window.$ = w.$
})(window)
沙箱模式中,把window或者其他内容当做参数传入沙箱的原因:
因为沙箱的本质就是要进行隔离,那么直接在沙箱中使用外界的内容会破坏这个隔离的基本原则,所以,将外界的东西通过传参的形式传入沙箱内进行使用,那么沙箱中使用的就完全都是沙箱自己的内容了。
有利于代码压缩,因为内置对象名称无法被压缩,如果作为参数传进来,那么我们使用的就是形参名,形参是可以被压缩的!
沙箱模式一般用于 框架、组件、插件等;
forEach方法和map方法
forEach方法是ES5新增的方法,可以用来快速遍历数组,它的回调函数有三个参数,下面来看看它的格式吧
var arr = [1,2,3,4,5];
arr.forEach(function(value,index,arr){
console.log(value);
})
// forEach语法用法如上所示;(arr参数一般用不到)
// 我们可以来扒一扒forEach方法实现原理;
//假装给数组原型上加一个我们自己的forEach方法
Array.prototype.myForEach = function (callback){
// 这里this.的指向-->被Array构造函数new出来的对象(我们要用的那个数组)
for(var i = 0; i < this.length; i ++){
callback(this[i], i, this);
}
}
// forEach的原理基本实现完了,是不是很简单
// 下面我们可以调用一下试试看;
var arr1 = [1,2,3,4,5];
arr1.myForEach(function(value,index){
console.log(value);
})
// 是不是也能用哈
再来看看map方法,其实map方法和forEach实现原理基本一样,只不过map方法有返回值,返回的返回的是和数组元素一一对应的另外一个数组(映射),map方法的返回值,其实就是将传给map的回调函数的每一次调用的返回值组合成一个新的数组,作为map的返回值
var arr = [1,2,3,4,5];
var result = arr.map(function(value,index){
console.log(value);
return value;
})
console.log(arr);
console.log(result);
//arr数组和result数组中存在的值都一样;
// 我们也可以来看看map方法的实现原理;
Array.prototype.myMap = function(callback){
var result = [];
for(var i = 0; i < this.length; i++){
var res = callback(this[i], i, this);
result.push(res);;
}
return result;
}
//值得一提的是map函数也可以传String Number等构造函数作为参数;
var arr1 = [1,2,3,4,5];
var res = arr1.myMap(String); // 数组每一项会变成string类型
console.log(res); //["1" , "2", "3", "4", "5"]
this的几种指向
1.
function test(){
console.log(this);
}
var obj = {};
obj.test = test;
obj.test(); // this指向window
2.
var obj = {
test: function(){
console.log(this);
}
}
var test = obj.test;
test(); // this指向window
new obj.test(); // this指向test函数创建出来的对象
3.
var arr = [
function(){
console.log(this);
},
function(){
console.log(this);
}
];
arr[0](); // this指向这个数组对象
4.
var length = 10;
function test(){
arguments[0]();
}
test(function(){
console.log(this.length); // 这里this指向第一个参数;
// 所以length是1(只有一个参数);
})
5. // 工厂模式创建对象,this的指向
function Person(name,age){
var obj = new Object();
obj.name = name;
obj.age = age;
console.log(this);
return obj;
}
var p = Person("小明",18); //这里的this指向window;
6. // 寄生模式创建对象,this的对象
function Person(name,age){
var obj = new Object();
obj.name = name;
obj.age = age;
console.log(this);
return obj;
}
ar p = new Person("张学友", 18);
// this的指向是被Person创建出来的对象
//这里值得一说的是,左后return obj了 构造函数的return语句如果return一个引用类型的数据是可以改变new默认的this指向的;
函数的四种调用模式
1. 函数名();
function test(){
console.log(this);
}
test();
2. 方法调用模式: 对象名.方法名(), 对象名方法名;
var obj = {
test: function(){
console.log(this);
}
}
obj.test();
obj["test"]();
3. 构造函数调用模式: new 函数名();
function Person(){
console.log(this);
}
var p = new Person();
4. 执行上下文调用模式;
执行上下文:我们在写一个方法的时候,总是会用到一个关键字this,而this的指向就是我们这里所说的执行上下文(执行环境);
1. call
- 调用call的对象必须是个函数function
- call的第一个参数将会是function改变上下文后指向的对象,
- 第二个参数开始可以接收任意个参数,这些参数将会作为function的参数传入function调用call的方法会立即执行
function test(a, b){
console.log(this);
return a + b;
}
var obj = {
name : "小红"
}
console.log(test.call(obj,1,2)); // 这里的this指向obj对象而不是window
- 这里说到 call, 不得不说 Object.prototype.toString.call()这个方法了
var arr = []
var str = Object.prototype.toString.call(arr);
console.log(str); //[object Array];
// 通过借用Object原型上的toString方法 把this指向arr;
// 这里我们可以模拟实现一下Obejct.prototype.toString.call方法
Obejct.prototype.myToString = function(){
var res = "[";
res += typeof this;
res += " ";
res += this.constructor.name;
res += "]";
return res;
}
var arr = [];
var date = new date()
var str = Object.prototype.myToString.call(arr);
console.log(str); //[obejct Array];
var str = Object.prototype.myToString.call(date);
console.log(str); // [object Date]
// 这样我们就基本实现了 Object.prototype.toString.call方法的原理了
// 主要利用call欺骗构造函数;
2. apply
- 与call方法的使用基本一致,但是只接收两个参数,其中第二个参数必须是一个数组或者类数组,这也是这两个方法很重要的一个区别;
function test(a, b){
console.log(this);
return a + b;
}
var obj = {
name : "小红"
}
console.log(test.call(obj,[1,2])); //this也是指向obj对象
call和apply方法使用应该注意的一些地方;
方法如果第一个参数不传,则函数中的this会指向window对象!
如果call/apply方法第一个参数传入的值类型的数据,则会自动将其转换成对应的引用类型的数据,再将this指向这个引用类型的数据!
如果call/apply方法第一个参数传入的是null或者undefined那么this将会指向window对象
具体情况我们看下面代码
1.
function test(a, b){
console.log(this);
return a + b;
}
var obj = {
name : "小红"
};
console.log(test.call(1,2)) //this会指向window
2.
function test(a, b){
console.log(this);
return a + b;
};
var obj = {
name : "小红"
};
console.log(test.call("a",1,2)) //this会指向String
3.
function test(a, b){
console.log(this);
return a + b;
};
var obj = {
name : "小红"
};
console.log(test.call(null,1,2)) //this会指向window
上下文调用模式的应用
- 可以将类数组转换为数组
var fakeArr = {
0: "a",
1: "b",
2: "c",
length: 3
};
var realArr = []
realArr = realArr.concat.apply(realArr, fakeArr);
realArr = realArr.push.apply(realArr, fakeArr);
realArr = Array.prototype.slice.call(fakeArr);
// 上述三种方法都可以让类数组转换为数组
// 讲到这里,我们也可以来模拟一下slice方法的实现原理
Array.prototype.mySlice = function (startIndex,endIndex){
var arr = [];
//这里应注意slice方法切割数组包括开头不包括结尾
startIndex = startIndex || 0;
endIndex = endIndex || arr.length;
for(var i = startIndex; i < endIndx; i++){
arr.push(this[i]);
}
return arr;
}
- 也可以用来求最大值
// 我们先用一般方法实现求最大值的功能
var arr = [22,33,4,666,12,22,34,56,21];
var max = arr[0];
for(var i = 1; i < arr.length; i++){
max = max > arr[i] ? max : arr[i];
}
console.log(max);
// 我们再用apply方法实现
max = Math.max.apply(Math,arr);
console.log(max);
// 用apply方法简单吧~~
- 我们也可以利用call欺骗构造函数来实现继承
function Person(){
//this 指向的是在Student中使用call方法调用Person的时候传进来的new创建的对象,所以这里通过this添加的所有的属性都添加给了Student中new创建的对象
this.name = "小明";
this.age = 18;
}
function Student(){
//this new创建的对象
Person.call(this);
//Person.call(new创建的对象)
//1. 调用Person函数
//2. 把Person函数中的this指向了new创建的这个对象
this.stuNo = 10086;
}
var stu = new Student();
console.log(stu.name); // 小明