JavaScript的函数劫持

一、什么是函数劫持?

        顾名思义,即在一个函数运行之前把它劫持下来,添加我们想要的功能。当这个函数实际运行的时候,它已经不是原本的函数了,而是带上了被我们添加上去的功能。这也是我们常见的【钩子函数】的原理之一。

二、常见的劫持方法

  1、系统内置功能的重写:就是将系统内置函数功能修改为自己想要实现的功能;也就是系统内置函数的改写 

var _log = console.log
console.log = function(str) {
	_log(str + " XX学苑")
}
console.log("hello")

2、this关键字的引用劫持:就是通过函数的一些特殊方法来自己指定函数的调用者

      (1)this的劫持1 —— call()方法

var obj = {
    name: "gang",
    fun: function(n1, n2) {
        var age = n1
        var height = n2
        console.log(this.name)
        console.log(age)
        console.log(height)
    }
}
var obj2 = {
    name: "shanghai"
}
obj.fun.call(obj2, 21, 180)

就是通过函数的call方法将对象obj中fun方法的调用者改写为对象obj2,也就是将fun方法中的this指向改为obj2,所以最终打印出来的this.name就是obj2的name: shanghai。  

        (2)this的劫持2 —— apply() 方法

var obj = {
    name: "gang",
    fun: function(n1, n2) {
        var age = n1
        var height = n2
        console.log(this.name)
        console.log(age)
        console.log(height)
    }
}
var obj2 = {
    name: "shanghai"
}
obj.fun.apply(obj2, [21, 180])

       apply()方法和call()方法的作用和用法基本一致,唯一的区别是call方法的参数是直接放进去,用逗号隔开,而apply方法的所有参数必须放在一个数组中再传入。

       这种方法也可以用来比较纯数字数组中的元素的最大值和最小值:

var arr = [10,230,40,6,2,5,3]
var maxnum = Math.max.apply(arr,arr) //相当于arr.max(10,230,40,6,2,5,3)
			
var minnum = Math.min.apply(arr,arr)//相当于arr.min(10,230,40,6,2,5,3)
console.log("max number = " + maxnum)
console.log("min number = " + minnum)

           (3)this的劫持3 —— bind()方法

var obj = {
    name: "shanghai"
}
var obj2 = {
    name: "gang",
    fun: function(n1, n2) {
        var age = n1
        var height = n2
        console.log(this.name)
        console.log(age)
        console.log(height)
    }.bind(obj)
}
obj2.fun(21, 180)

         bind()方法就是在函数设计的时候就指定函数的调用者,也就是指定函数中this的指向;其参数传入形式和call方法一样,是直接传入参数。

   三、JavaScript数据劫持的两个API

       (1)Object.defineProperty(target, key, desc)(vue2使用)

                     参数target: 目标对象

                     参数key: 将要操作的对象中的属性或名称

                     参数desc: 对象的描述

扫描二维码关注公众号,回复: 14632282 查看本文章

           Object.defineProperty()用来访问一个对象的设置,允许精确地添加或修改对象的属性

           劫持即是通过Object.defineProperty()对对象属性的set和get操作与检测

var data = {
    name:'xiaoqiao'
 }
 Object.keys(data).forEach(function(key){
    Object.defineProperty(data,key,{
        //enumerable: false,可否被枚举/遍历
        //writable: false,读写控制
        //configurable: false,可否重新定义属性,可否被删除
        get:function(){
           console.log('get');
       },
       set:function(){
           console.log('监听到数据发生了变化');
       }
   })
 });
 data.name //控制台会打印出 “get”
 data.name = '上海' //控制台会打印出 "监听到数据发生了变化"

           (2)Proxy(target, handler)(vue3使用)          

                            参数target: 目标对象

                            参数handler: 对象处理器

             相比Object.defineProperty(),速度更快,更重要的是,vue3因为它可以响应数组变化了

const data = {
    bao: {
      price: 5000,
      name: "LV"
    },
    list: [1, 2]
  };
  const handler = {
    get: function(target, key) {
      if (target[key] > 5000) {
        console.log("太贵了,不买了");
        return 1;
      } else {
        console.log("可以接受,买买买");
        return 3;
      }
    }
  };
  const handler2 = {
    get: function(target, key) {
      console.log("读到的下标", key);
      return target[key];
    }
  };
// 读取对象
  const p = new Proxy(data.bao, handler);
  const value = p.price; // 此操作触发get,控制台打印"符合客户心理预期,买买买"
  console.log(value); // 此时的 value 应是 3,因为get拦截了取值操作,并返回了新值
//读取数组
  const p2 = new Proxy(data.list, handler2);
  const value2 = p2[0] // 此操作触发get 控制台打印出:读到的下标, 0
  console.log(value2) // 此时的 value2 应是 1

猜你喜欢

转载自blog.csdn.net/m0_37911706/article/details/127461052
今日推荐