JavaScript高级学习(四)

       上一篇JavaScript高级学习(三)主要学习了原型链和继承,除此以外还夹杂了一些其他知识,例如this指向问题,这一篇我们主要学习apply、call、bind方法,以及闭包等知识。

apply()、call()、bind()

1.apply和call

function f1(x,y){
	console.log((x+y)+"====>"+this);
}
f1(10,20);

//此时的f1实际上被当成对象来使用
f1.apply();
f1.call();
//apply和call没有传参数

在这里插入图片描述
call和apply方法也是函数调用的方式。
apply和call方法中如果没有传入参数,或者传入的是null,那么调用该方法的函数对象中的this就是函数本身指向的this。

apply(thisArg,[array]);
call(thisArg,args);

apply和call方法都可以让函数或者方法来调用,传入参数和函数自己调用的写法不一样,但是效果是一样的。
例子:

function f1(x,y){
	console.log((x+y)+"====>"+this);
}
f1(10,20);
var obj={
	age:10,
	sex:"男"
};
f1.apply(obj,[20,5]);
f1.call(obj,20,6);

在这里插入图片描述

    function Person(age,sex) {
      this.age=age;
      this.sex=sex;
    }
    //通过原型添加方法
    Person.prototype.sayHi=function (x,y) {
      console.log("您好啊:"+this.sex);
      return 1000;
    };
    var per=new Person(10,"男");
    per.sayHi();

    console.log("==============");
    function Student(name,sex) {
      this.name=name;
      this.sex=sex;
    }
    var stu=new Student("小明","人妖");
    var r1=per.sayHi.apply(stu,[10,20]);
    var r2=per.sayHi.call(stu,10,20);

    console.log(r1);
    console.log(r2);

在这里插入图片描述
总结

  • apply和call都可以改变this的指向
  • 作用 :函数调用、改变this指向
  • 使用方法
方法/函数名字.apply(对象,[参数1,参数2...]);
方法/函数名字.call(对象,参数1,参数2...);
  • 异同:
    同:都可以改变this指向 ,调用函数
    异:参数传递方式不一样
  • 只要是想使用别的对象的方法,并且希望这个方法是当前对象的,那么就可以使用apply和call方法改变this的指向
  • apply和call方法实际上并不在函数这个实例对象中,而是在Function的prototype中
    function f1() {
      console.log(this+":====>调用了");
    }
    console.dir(f1);
    //对象调用方法,说明,该对象中有这个方法
    f1.apply();
    f1.call();
    console.log(f1.__proto__==Function.prototype);
    //所有的函数都是Function的实例对象
    console.dir(Function);

在这里插入图片描述
2.bind

bind(thisArg,args);//返回值为函数
 function f1(x, y) {
      console.log((x + y) + ":=====>" + this);
    }
 var ff=f1.bind(null);
 ff(10,20);//30:=====>[object Window]

bind复制,复制一份的时候,把参数传入到了f1函数中,x===>10,y===>20,null就是this,默认就是window

bind方法是复制的意思,参数可以在复制的时候传进去,也可以在复制之后调用的时候传入进去

apply和call是调用的时候改变this指向
bind方法,是复制一份的时候,改变了this的指向

方法/函数名字.bind(对象,参数1,参数2,...);---->返回值是复制之后的这个函数
函数中的几个成员

函数中有的属性:

  • name:函数的名字,name是只读的,不能修改
  • arguments:实参的个数
  • length:函数定义的形参个数
  • caller:调用
 function f1(x,y){
      console.log(f1.name);
      console.log(f1.arguments.length);
      console.log(f1.length);
      console.log(f1.caller);//调用者
 }
 f1(10,20,10,6);//f1   4  2  null
 function f2() {
      f1(1,2);
    }
 f2();//f1  2  2  f2

在这里插入图片描述

函数作为参数、函数作为返回值

1.函数作为参数

function f1(fn){
	fn();
}
//fn是参数,作为函数使用了,函数可以为参数
  • 传入匿名函数
 function f1(fn){
	fn();
 }
 f1(function () {
    console.log("我是匿名函数");
 });
  • 传入命名函数
    函数作为参数,如果是命名函数,那么只传入命名函数的名字,不加括号
 function f1(fn){
 	  fn();
 }
 function f2() {
      console.log("f2的函数");
 }
 f1(f2);//f2的函数
  • 定时器中传入函数
function f1(fn) {
     setInterval(function () {
        console.log("定时器开始");
        fn();
        console.log("定时器结束");
      },1000);
 }
f1(function () {
      console.log("好困啊,好累啊,就是想睡觉");
});

2.函数作为返回值

 function f1() {
      console.log("f1函数开始");
      return function () {
          console.log("我是函数,但是此时是作为返回值使用的");
      }
 }   
 var ff=f1();
 ff();

例子:
判断某个对象类型是不是传入的类型

补充知识点:
var num=10;
console.log(typeof num);//获取num这个变量的数据类型
var obj={};
console,log(obj instanceof Object);//true
//输出Object的数据类型
console.log(Object.prototype.toString());//[object Object]
//输出数组的数据类型
console.log(Object.prototype.toString.call([]));//[object Array]

想要获取某个对象的数据类型
Object.prototype.toString.call(对象);

function getFunc(type){
	return function (obj){
	   return Object.prototype.String.call(obj)===type;
	}
}
var ff=getFunc("[object Array]");
var result=ff([10,20,30]);
console.log(result);///true

3.排序
排序—函数作为参数使用,匿名函数作为sort方法的参数使用

   var arr = [1, 100, 20, 200, 40, 50, 120, 10];
        //排序
    arr.sort();
    console.log(arr);//[1,10,100,120,20,200,40,50]

sort()不是稳定的排序方法

    var arr = [1, 100, 20, 200, 40, 50, 120, 10];
    arr.sort(function (obj1,obj2) {
      if(obj1>obj2){
        return -1;
      }else if(obj1==obj2){
        return 0;
      }else{
        return 1;
      }
    });
    console.log(arr);//[200,120,100,50,40,20,10,1]

	var arr1=["acdef","abcd","bcedf","bced"];
    arr1.sort(function (a,b) {
      if(a>b){
        return 1;
      }else if(a==b){
        return 0;
      }else{
        return -1;
      }
    });
    console.log(arr1);//["abcd","acdef","bced","bcedf"]
闭包

1.作用域,作用域链

变量:

  • 局部变量
  • 全局变量

作用域:变量的使用范围

  • 局部作用域
  • 全局作用域

JS中没有块级作用域-------->块级作用域: 任何一对花括号({和})中的语句集都属于一个块,在这之中定义的所有变量在代码块外都是不可见的

   //全局变量
    while(true){
      var num=10;
      break;
    }
    console.log(num);//10

    {
      var num2=100;
    }
    console.log(num2);//100

    if(true){
      var num3=1000;
    }
    console.log(num3);//1000
    
    //局部变量
    function f1() {
      var num=10;
    }
    console.log(num);//error

作用域链:变量的使用,从里到外,层层搜索,搜索到了就可以直接使用

   var num=10; //作用域链 级别:0
   function f1() {
     var num=20;//作用域链 级别:1
     function f2() {
       var num3=30;//作用域链 级别:2
       console.log(num);//30
     }
     f2();
   }
   f1();

层层搜索,搜索到0级作用域的时候,如果还是没有找到这个变量,结果就是报错
2.预解析
就是在浏览器解析代码之前,把变量和函数的声明提前(提升)到该作用于的最上面

    //变量的提升
    //var num;
    console.log(num);//undefined
    var num=100;

    //函数的声明被提前了
    f1();//这个函数,执行了
    function f1() {
      console.log("这个函数,执行了");
    }
	//命名函数
    //var f2;声明提前,f2相当于undefined,undefined+()不正确
    f2();
    f2=function () {
      console.log("小杨好帅哦");
    };//error
闭包
  • 概念:函数A中,有一个函数B,函数B中可以访问函数A中定义的变量或者是数据,此时形成闭包。(不严谨)
  • 模式:函数模式的闭包、对象模式的闭包
  • 作用:缓存数据,延长作用域链
  • 优缺点:缓存数据

1.模式
函数模式的闭包:在一个函数中有另一个函数

//例1
function f1(){
	var num=10;
	function f2(){
		console.log(num);
	}
	f2();
}
f1();//10

//例2
function f1(){
	var num=10;
	return function(){
		console.log(num);
	}
}
var ff=f1();
ff();//10

对象模式的闭包:函数中有一个对象,对象访问外层函数定义的变量或数据

//例1
function f1(){
	var num=10;
	var obj={
		age:num,
		sex:"男"
	};
	console.log(obj.num);
}
f1();//10

//例2
function f1(){
	var num=10;
	return {
		age:num,
		sex:"男"
	}
}
var ff=f1();
console.log(ff.age);//10

2.作用
闭包的作用是:缓存数据,延长作用域链
以下例子显示必闭包的作用

//普通函数
function f1(){
	var num=10;
	num++;
	console.log(num);
}
f1();//11
f1();//11
f1();//11

//闭包函数
function f1(){
	var num=10;
	return function(){
		num++;
		return num;
	}
}
var ff=f1();
console.log(ff());//11
console.log(ff());//12
console.log(ff());//13

以上例子显示了闭包的作用——缓存数据
普通函数多次调用时,num总重新赋值(var num=10)
闭包函数中,ff函数中没有重新赋值,因为数据缓存,在之前的num基础上+1

3.案例
闭包:产生多个相同的随机数

function f1(){
	var num=parseInt(Math.random()*10+1;//1-10随机数
	return function(){
		console.log(num);
	}
}
var ff=f1();
ff();
ff();
ff();
//3个结果相同

4.总结
如果想要缓存数据,就把这个数据放在外层的函数和里层的函数的中间位置。

闭包作用:缓存数据,优点也是缺点,不能及时释放

局部变量是在函数中,函数使用结束后,局部变量就会自动释放
闭包中,里面的局部变量的使用作用域链会延长

沙箱

沙箱:环境,黑盒,虚拟的环境中,在一个虚拟的环境中模拟真实世界,做实验,实验结果和真实世界结果是一样的,但是不会影响真实世界。

    var num=10;
    console.log(num+10);//20

    //沙箱---小环境
    (function () {
      var num=10;
      console.log(num);//10
     })();

    //沙箱---小环境
    (function () {
      var num=20;
      console.log(num+10);//30
    }());

    var num=100;
    (function () {
     var num=10;
      console.log(num);//10
    }());
    console.log(num);//100
递归

函数中调用函数自己,就是递归。递归函数中,一定要有结束条件

案例
求n个数字的累加和

   //函数的声明
    function getSum(x) {
      if(x==1){
        return 1;
      }
      return x+getSum(x-1);
    }
    //函数的调用
    console.log(getSum(5));//15

求一个数字各个位数上的数字之和

   function getEverySum(x) {
      if(x<10){
        return x;
      }
      //获取的是这个数字的个位数
      return x%10+getEverySum(parseInt(x/10));
    }
   console.log(getEverySum(1364));//14
浅拷贝和深拷贝

在之前学习继承的时候,大概的看了以下浅拷贝,现在继续学习浅拷贝和深拷贝。

1.浅拷贝
拷贝就是复制,相当于 把一个对象中所有的内容,都复制一份给另一个对象,直接复制,或者说,就是把一个对象的地址给了另一个对象,他们的指向相同,两个对象之间有共同的属性或者方法都可以使用。

var obj1={
	age:10,
	sex:"男"
}
var obj2={};
function extend(a,b){
	for(var key in a){
		b[key]=a[key];
	}
}
extend(obj1,obj2);

2.深拷贝
把一个对象中所有的属性或者方法,一个一个的找到,并且在另一个对象中开辟相应的空间,一个一个的存储到另一个对象中

var obj1={
	age:10,
	sex:"女",
	car:["奔驰","宝马","特斯拉","奥拓"],
	dog:{
		name:"大黄",
		age:5,
		color:"黑白色"
	}
};
var obj2={};
function extend(a,b){
	for(var key in a){
		var item=a[key];
		if(item instanceof object){
			b[key]={};
			extend(item,b[key]);
		}else if(item instanceof  Array){
			b[key]=[];
			extend(item,b[key]);
		}else{
			b[key]=item;
		}
	}
}
extend(obj1,obj2);
总结
  • apply、call使用和区别
    都可以改变this指向(使用其他函数/对象的方法)
    方法/函数名字.apply(对象,[属性1,属性2…]);
    方法/函数名字.call(对象,属性1,属性2…);
    其他对象.方法名.apply(对象,[属性1,属性2…]);
    其他对象.方法名.call(对象,属性1,属性2…);
  • bind方法的使用和区别
    复制一个方法或函数,在复制的同时改变了this指向
    方法/函数名字.bind(对象,属性1,属性2…);
    返回值是复制之后的这个函数
  • 高阶函数
    函数的使用方式:函数作为参数、函数作为返回值
    函数为参的时候:可以直接使用匿名函数,也可以是命名函数
  • 作用域和作用域链及预解析
  • 闭包
    函数中有另一个函数,或者是一个函数中有另一个对象,里面的函数或对象都可以使用外面函数中定义的变量或数据,此时形成闭包。
    闭包模式:函数模式的闭包、对象模式的闭包
    闭包作用:缓存数据,延长作用域链,同时也是缺点,函数中的变量不能及时的释放。
  • 沙箱
    就是一个环境,也叫黑盒。在这个环境中模拟外面真实的开发环境,完成需求,效果和真实的开发环境效果相同
    优点:避免命名冲突
  • 递归
    函数中掉调用函数自己,递归一定要有结束条件,否则就是死循环
    一般用在遍历上
    缺点:效率低
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值