接上篇:[ES6]Day08—Object对象的拓展
即利用function
关键字声明函数
function fn(){};
即利用函数表达式
声明函数
const fn=function(){};
() => {};
语法:var fn = new Function('arg1',' arg2',‘...argN‘,’'func')
参数 | 含义 |
---|---|
’ arg1’, ‘arg2’,’…argN’ | 为调用func函数需要传入的参数,每个参数都要单引号括起来,需要传字符串类型,用逗号分隔, |
func | 函数体,需要单引号括起来,需要传字符串类型 |
const fn = new Function('a','b','console.log(123);console.log(a+b)');
fn(1,2);
console.log(fn instanceof Object); //true
function fn(){
console.log(123);
};
fn();
//fn.call();
const obj={
say:function(){
console.log(123);
}
}
obj.say()
function Star(){
console.log(123);
}
new Star();
btn.onclick=function(){console.log(123)}; //点击了按钮就能调用
setInterval(function(){
console.log(123);
},1000)
//每隔一秒自行调用
()()
(function(){
console.log(123);
})() // 自动调用
函数内的this
指向,是当我们调用函数时确定的,调用方式的不同决定了this
的指向不同,一般指向我们的调用者
调用方式 | this 指向 |
---|---|
普通函数调用 | window |
构造函数调用 | 实例对象 原型对象里的方法也指向实例对象 |
对象方法调用 | 该方法所属对象 |
事件绑定方法 | 绑定事件的对象 |
定时器函数 | window |
立即执行函数 | window |
bind()
、call()
、apply()
三种函数可以改变this
指向
语法:func.call(thisArg, arg1, arg2, ...argN)
参数 | 含义 |
---|---|
thisArg |
this 的指向对象 |
arg1, arg2, ...argN |
为调用func函数需要传入的参数,全都用逗号分隔,直接放到后面 |
作用:调用 func 函数
,并改变
函数运行时的this指向
例子:
function fn(x,y){
console.log(this);
console.log(x+y);
}
var o={
name:'sam'
}
fn.call(o,3,8)
语法:func.apply(thisArg, [argsArray])
参数 | 含义 |
---|---|
thisArg |
必选的。在 func 函数运行时使用的 this 值。请注意,this可能不是该方法看到的实际值:如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动替换为指向全局对象,原始值会被包装。 |
[argsArray] |
可选的。但是必须是一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 func 函数。如果该参数的值为 null 或 undefined,则表示不需要传入任何参数。从ECMAScript 5 开始可以使用类数组对象。 |
作用:apply()
方法调用一个参数为数组(或类似数组对象)
,给定this值的函数。
例子:
function fn(x,y){
console.log(this);
console.log(x+y);
}
var o={
name:'sam'
}
fn.apply(o,[3,8]) //输出 {name:'sam'} 11
fn.apply(o,3,8) //报错 ,因为第二个参数不是数组或类数组
var arr=[1,54,78,3,6,85,100]
Math.max.apply(Math,arr); //不改变this指向,求数组最大值
//100
语法:let boundFunc = func.bind(thisArg, arg1, arg2, ...argN)
参数 | 含义 |
---|---|
thisArg |
this 的指向对象 |
arg1, arg2, ...argN |
调用func函数需要传入的参数,全都用逗号分隔,直接放到后面,返回一个函数(闭包),函数延迟执行,需要调用才执行 |
return | 返回由指定的this值和初始化参数改造后的原函数拷贝 ,相当于返回一个新的函数 |
作用:bind()不会调用函数,但是能改变函数内部的this指向。 如果有的函数不需要立即调用,但是又想改变这个函数内部的this指向此时用bind()
例子:
function fn(x,y){
console.log(this);
console.log(x+y);
}
var o={
name:'sam'
}
fn.bind(o,3,8) // 输出fn 函数,不会执行
fn.bind(o,3,8)() //执行 fn 函数, 输出 {name:'sam'} 11
实例: 有一个按钮,当我们点击之后,禁用按钮,3秒钟后开启按钮
var btn =document.querySelector('button');
btn.onclick=function(){
this.disabled=true; //这个this 指向btn 按钮
console.log(this);//定时器函数里面的 this 指向 btn
//setTimeout(function(){
// console.log(this);//定时器函数里面的 this 指向window
//},3000)
setTimeout(function(){
console.log(this);//加了bind(),定时器函数里面的 this 指向 btn
}.bind(this),3000) //this 在定时器外面,指向的是btn对象 ,将btn对象绑定到定时器上
}
}
多个按钮
改变定时器中的this指向,原来是指向Window,改变之后指向btn ,而且不会立即调用函数
var obj={
name:'张三',
age:18,
myFun:function(fm,to){
console.log(this.name+" 年龄"+this.age,fm + "去往" + to);
}
}
var db={
name:'德玛',
age:99
}
obj.myFun.call(db,'成都','上海'); //德玛 年龄99 成都去往上海
obj.myFun.apply(db,['成都','上海']); //德玛 年龄99 成都去往上海
obj.myFun.bind(db,'成都','上海'); // 返回一个函数,这里就是 bind 和 call/apply 的区别之一,bind 的时候不会立即执行
obj.myFun.bind(db,'成都','上海')(); //德玛 年龄99 成都去往上海
obj.myFun.bind(db,['成都','上海'])(); //德玛 年龄99 成都,上海去往undefined
相同点
1、都可以改变上下文 this 指向的,第一个参数都是 this 的指向对象
2、三者的参数不限定是string类型,允许是各种类型,包括函数 、 object
不同点
1、call()
、apply()
会调用函数,并且改变函数内部this指向。
2、传递参数形式不同,call()
后面的参数全都用逗号分隔 ,apply()
后面的参数必须传一个数组
obj.myFun.call(db,'成都', ... ,'string' );
obj.myFun.apply(db,['成都', ..., 'string' ]);
3、bind()
不会调用函数,除了返回的是原函数改变this之外产生的新函数
以外,它的参数和call()
一样。如果有的函数不需要立即调用,但是又想改变这个函数内部的this指向此时用bind()
方法
4、应用对象不同,call()
、apply()
是直接使用在函数上,而 bind()
绑定 this 后返回执行上下文被改变的函数(闭包),不会立即执行,需要()
才会执行。
5、主要应用场景不同,call()
常用在继承;apply()
经常与数组有关,比如借助于数学对象实现数组最大值最小值;bind()
不调用函数,但是还想改变this指向。比如改变定时器内部的this指向。
Array.prototype.sum=function(){
var sum=0;
for(let i = 0; i < this.length ; i++){
sum+=this[i];
}
return sum;
}
var arr=new Array(1,2,3)
arr.sum() //6
//打印 Array.prototype
num=20
console.log(num) //undefined
var num=10;
console.log(num);
delete num; //false
在正常的全局作用域中 this
指向 window对象
function fn(){
console.log(this);
}
fn(); //window
严格模式下全局作用域中函数中的 this
是 undefined
function Star(){
this.sex='男';
}
var yy= new Star();
console.log(yy.sex); //男
setTimeout(function(){
console.log(this); //window
},10000)
'use strict'
function fn(a,a){
console.log(a+a)
}
fn(1,2)
"use strict";
if (true) { //非函数、非脚本
function f() { } // !!! 语法错误
f();
}
for (var i = 0; i < 5; i++) {
function f2() { } // !!! 语法错误
f2();
}
function baz() { // 合法
function eit() { } // 同样合法
}
想了解更详细看这里 严格模式-MDN
高阶函数是对其他函数进行操作的函数,至少有这两大特征之一:
函数也是一种数据类型,同样可以作为参数,传递给另一个参数使用,最典型的就是作为回调函数。
//接收函数作为参数
function fn(callback){
callback&&callback();
}
fn( function(){ alert('hi') } )
//将函数作为返回值输出
function fn(){
return function (){}
}
fn();
变量根据作用域的不同分为两种:全局变量和局部变量。
闭包
是指有权访问另一个函数作用域中变量
的 函数。
简单来说,一个作用域可以访问另外一个函数内部的局部变量
闭包
的作用:延伸了变量的作用范围
例子:
function fn(){
var num = 10;
function fun(){
console.log(num);//访问 fn 函数中的 num 变量
}
fun();//打印num
}
//思考:怎么在外面能够访问到fn 函数中的 num 呢
解决思路:
将 fun
函数作为fn
函数的返回值,用一个变量f
在外部接收,由于返回值为一个函数,所以调用 f()
就可以得到 num 的值
function fn(){
var num = 10 ;
//function fun(){
// console.log(num); //访问fn 内部的局部变量
//}
//return fun;
return function fun(){// 直接 return
console.log(num); //访问fn 内部的局部变量
}
}
var f = fn();
//类似于
//var f= function fun(){
// console.log(num);
//}
f(); //调用 f(), 就可以访问 fn 内部的局部变量num了
异步函数必须调用才会执行,所以在 for 循环 创建 立即执行函数 ()(i)
存当前循环的 i
值,在立即执行函数中写 点击事件。
但是闭包会产生内存泄漏
问题,因为创建了 立即执行函数 ()(i)
,应该要马上销毁变量 i
,但是这里要等点击事件执行了才能销毁变量 i
setTimeout()
也是异步任务的一种,要等3秒到了才会执行
// 打车起步价13(3公里内),之后每多一公里增加5块钱,用户输入公里数就可以计算打车价格
//如果有拥堵情况,总价格多收取10块拥堵费
//调用
car.price(7) //7公里 (7-3)*5+13=33
car.yd(true) // 33+10=43
var name = "The Window";
var object = {
name:"My Object",
getNameFunc : function(){
return function(){ //匿名函数
return this.name;
}
}
}
console.log(object.getNameFunc()) //返回一个函数
console.log(object.getNameFunc()()) //执行this.name
//相当于立即执行函数,this指向了window
function(){ console.log(this) } ()
所以执行下面这段代码:
function(){ console.log(this.name) } () //结果是 window.name = "The Window"
综上所述这道题 没有局部变量的访问,不存在闭包(有点坑,哈哈哈),最终答案为"The Window"。
var name = "The Window";
var object = {
name:"My Object",
getNameFunc : function(){
var that = this;
return function(){ //匿名函数
return that.name;
}
}
}
console.log(object.getNameFunc()) //返回一个函数
console.log(object.getNameFunc()()) //执行that.name
这里的 that 指向 object 这个对象,所以 that.name =“My Object” ,这道题存在一个闭包就是getNameFunc()
闭包
是一个作用域可以访问另外一个函数的局部变量的函数
延伸变量的作用范围
如果一个函数在内部可以调用其自身
,那么这个函数 就是 递归函数
,简单来说,就是函数内部自己调用自己。
递归函数的作用与循环一样,容易发生“栈溢出” 错误(stack overflow),所以必须要加上退出条件 return
//正确写法
var num=1;
function fn(){
console.log(num) //1,2,3,4
if(num==4) return;
num++;
fn();
}
fn();
function fn(n){
if(n==1) return 1;
return n*fn(n-1)
}
fn(6) //720
用户输入一个数字n ,可以得到这个数字对应的兔子序列值
1+1=2,1+2=3,2+3=5,3+5=8,5+8=13 … 相邻两个数相加等于第三个数
第三个数=前两项之和 n=(n-1)+(n-2)
function fb(n){
if(n===1 ||n===2){
return 1;
}
return fb(n-1) +fb(n-2);
}
console.log(fb(3)) //第3个是 2
console.log(fb(6)) //第6个是 8
输入商品id ,找到商品信息
var arr=[
{
id:0,
name:'水果',
goods:[{
id:11,
gname:'西瓜' ,
goods:[
{
id:111,
gname:'妈妈牌'
}
]
},{
id:12,
gname:'草莓'
}]
},
{
id:1,
name:'服饰',
goods:[{
id:13,
gname:'上衣'
},{
id:14,
gname:'裙/裤'
}]
}];
var o ={}
function getObjItem(id,arr){
//1、用forEach 遍历数组
arr.forEach(item=>{
if(item.id==id){
o = item //这里只能得到第一层的
return item
}else if( item.goods && item.goods.length>0){
//2、取到里层的数据,使用递归
o = getObjItem(id,item.goods)
}
});
return o
}
getObjItem(111,arr)