JavaScript—ES6 元编程(5)

2023-11-08

几年前 ES6 刚出来的时候接触过 元编程(Metaprogramming)的概念,不过当时还没有深究。在应用和学习中不断接触到这概念,比如 mobx 5 中就用到了 Proxy 重写了 Observable 对象,觉得有必要梳理总结一下。

本文并非是一篇传统意义上的教程,总结的是自己学习 ES6 元编程相关知识(Symbols & Proxy & Reflect)的理解、教程文档 和 代码片段。

1、理解元编程
Symbol、Reflect 和 Proxy 是属于 ES6 元编程范畴的,能“介入”的对象底层操作进行的过程中,并加以影响。元编程中的 元 的概念可以理解为 程序 本身。

”元编程能让你拥有可以扩展程序自身能力“。这句话还是很抽象,初学者该怎么理解呢?

我也理解了半天,想到了下面的例子:

就好比你原本是公司的部门的大主管,虽然你能力很强,但也必须按照规章制度做事,比如早上 8 点必须到公司,否则你就要扣绩效;而后来公司基本规定灵活了,每个部门可以自己制定打卡制度,此时身为主管的你,依据公司该基本规定,制定自己部门的考勤制度,本部门的职工可以 9 点来公司,还可以不打卡!(当然还可以制定其他规定)

在这个例子中:

”整个公司“就相当于 JS 引擎
”公司的基本规章制度“就相当于 JS 运行机制和语法,员工办事最低要求就是遵照公司的规章制度
”在此基础上,你拥有管理部门的权力,负责开发并维护一些产品“,这种行为就相当于平时普通的编程;
公司的基本规定变灵活之后,你除了拥有以前管理员工的权力之外,”还拥有更改制度(针对该部门)的能力,这样就可以从制度层面影响员工的最低要求行为“,这里更改规章制度就相当于 元编程 了;
这里的例子不一定准确,是我个人的理解,权做参考,也可以去看看知乎上 怎么理解元编程? 的问答。

借助这个例子理解元编程,我们能感知在没有元编程能力的时候,就算你编程能力很厉害,但终究“孙悟空翻不出五指山”;而掌握了元编程能力之后,就差上天了,“给你一个支点,你就能撬动地球”,能力大大扩增。

简言之,元编程让你具备一定程度上改变现有的程序规则层面的能力。或者说,元编程可以让你以某种形式去影响或更改程序运行所依赖的基础功能,以此获得一些维护性、效率上的好处。

Javascript 中,eval、new Function()便是两个可以用来进行元编程的特性。不过因为性能和可维护的角度上,这两个特性还是不要用为妙。

在 ES6 之后,标准引入了 Proxy & Reflect & Symbols,从而提供比较完善的元编程能力。

2、学习 ES6 元编程的资源
我原本也想仔细讲讲 ES6 中 Symbol、Proxy 和 Reflect 的基本概念和使用的,但网上这方面的文章不要太多,觉得重复码字也没有太必要。这里着重推荐几篇,分为教程类和手册类,通读完之后应该就掌握差不多了。

元编程在 ES6 体现最为突出的是 Proxy 的应用,目前我所找的文章也多偏向 Proxy。

原理教程类:

深入浅出ES6(十二):代理 Proxies:ES6 深入浅出系列,个人推荐认真读完该文章。本文的作者实现了 ES6 的 Reflect 特性,所以他对 ES6 这两个特性理解是最为深刻的,他的文章自然要深度阅读。
ES6 Proxies in Depth:和其他教程相比,该文章篇幅稍微短一些,能较为快速得掌握概念和一些实际应用。
Metaprogramming with proxies:来自 《Exploring ES6》书籍摘选,基础入门。
Chapter 7: Meta Programming:经典的 《You Don't Know JS》系列文章,深入浅出,文章够长,需要一些耐心。
Metaprogramming in ES6: Symbols and why they're awesome:本篇就是基于 Symbols、Reflect、Proxy 等实现元编程的教程系列教程,内容也足够详实。
ES6学习笔记: 代理和反射:非常详实地整理了 Proxy 和 Reflect 相关的知识点,只是阅读起来略微枯燥。
应用教程类:

ES6 Features - 10 Use Cases for Proxy:收集了 10 个 proxy 的具体应用场景,具体的代码放在 jsProxy 仓库中
从ES6重新认识JavaScript设计模式(五): 代理模式和Proxy:本文从设计模式上去理解 Proxy 的应用
使用 Javascript 原生的 Proxy 优化应用 :文章涉及到 Proxy 的基本用法、如何使用 Proxy 创建代理模式,以及如何对应用进行优化。
手册类:

MDN - Proxy:MDN 上的 Proxy 官方文档
MDN - Reflect:MDN 上的 Reflect 官方文档
MDN - 元编程:MDN 官方文档教程,介绍了元编程的概念,应该算是比较抽象的,当手册翻翻不错;
ECMAScript 6 入门 - Proxy:阮一峰翻译的 《ECMAScript 6 入门》 教程
ES6 自定义JavaScript语言行为的 Proxy 对象:算是简明版的中文版的 API 手册
在没充分理解元编程之前翻手册还是挺枯燥的,建议平时使用的时候再从这里补漏
随着时间的推移,上面收集的文章可能会显得陈旧,又有可能出现新的好文章,推荐在搜索引擎中使用 js Metaprogramming 或者 es6 proxy 进行搜索相关文章;

3、代码片段
下面摘抄一些代码片段,方便自己后续在应用 JS 元编程的时候快速 "借鉴"。你们如果也有觉得不错的代码片段,欢迎在 issue 中回复,我将不定期更新到这儿。

目录
Schema 校验
自动填充对象
进制转换
缓存代理
实现私有属性
函数节流
图片懒加载
监听属性更改
实现单例模式
Python 那样截取数组

Schema 校验 ↑
示例来自 ES6 Proxies in Depth
场景:person 是一个普通对象,包含一个 age 属性,当我们给它赋值的时候确保是大于零的数值,否则赋值失败并抛出异常。

var person = { age: 27 };

思路:通过设置 set trap,其中包含了对 age 字段的校验逻辑。

代码:


var validator = {
  set (target, key, value) {
    if (key === 'age') {
      if (typeof value !== 'number' || Number.isNaN(value)) {
        throw new TypeError('Age must be a number')
      }
      if (value <= 0) {
        throw new TypeError('Age must be a positive number')
      }
    }
    return true
  }
}
var proxy = new Proxy(person, validator)
proxy.age = 'foo'
// <- TypeError: Age must be a number
proxy.age = NaN
// <- TypeError: Age must be a number
proxy.age = 0
// <- TypeError: Age must be a positive number
proxy.age = 28
console.log(person.age)
// <- 28

自动填充对象 ↑

场景:创建一个Tree()函数来实现以下特性,当我们需要时,所有中间对象 branch1、branch2 和 branch3 都可以自动创建。


var tree = Tree();
tree
//    { }
tree.branch1.branch2.twig = "green";
// { branch1: { branch2: { twig: "green" } } }

tree.branch1.branch3.twig = "yellow";

// { branch1: { branch2: { twig: "green" },
//                 branch3: { twig: "yellow" }}}

思路:Tree 返回的就是一个 proxy 实例,通过 get trap ,当不存在属性的时候自动创建一个子树。

代码:


  function Tree() {
    return new Proxy({}, handler);
  }
  var handler = {
    get: function (target, key, receiver) {
      if (!(key in target)) {
        target[key] = Tree();  // 自动创建一个子树
      }
      return Reflect.get(target, key, receiver);
    }
  };

进制转换 ↑
场景:比如将 2 进制转换成 16 进制或者 8 进制,反之也能转换。

思路:由于大部分的功能是相同的,我们通过函数名字将变量提取出来,然后通过 get trap 完成进制转换。

代码:


const baseConvertor = new Proxy({}, {
  get: function baseConvert(object, methodName) {
    var methodParts = methodName.match(/base(\d+)toBase(\d+)/);
    var fromBase = methodParts && methodParts[1];
    var toBase = methodParts && methodParts[2];
    if (!methodParts || fromBase > 36 || toBase > 36 || fromBase < 2 || toBase < 2) {
      throw new Error('TypeError: baseConvertor' + methodName + ' is not a function');
    }
    return function (fromString) {
      return parseInt(fromString, fromBase).toString(toBase);
    }
  }
});

baseConvertor.base16toBase2('deadbeef') === '11011110101011011011111011101111';
baseConvertor.base2toBase16('11011110101011011011111011101111') === 'deadbeef';

缓存代理 ↑

场景:以没有经过任何优化的计算斐波那契数列的函数来假设为开销很大的方法,这种递归调用在计算 40 以上的斐波那契项时就能明显的感到延迟感。希望通过缓存来改善。

const getFib = (number) => {
  if (number <= 2) {
    return 1;
  } else {
    return getFib(number - 1) + getFib(number - 2);
  }
}

注:这只是演示缓存的写法,递归调用本身就有问题,容易导致内存泄露,在实际应用中需要改写上述的 getFib 函数。

思路:因为是函数调用,所以需使用 apply trap,利用 Map 或者普通对象存储每次计算的结果,在执行运算前先去 Map 查询计算值是否被缓存。(相当于以空间换时间,获得性能提升)

代码:


const getCacheProxy = (fn, cache = new Map()) => {
  return new Proxy(fn, {
    apply(target, context, args) {
      const argsString = args.join(' ');
      if (cache.has(argsString)) {
        // 如果有缓存,直接返回缓存数据
        console.log(`输出${args}的缓存结果: ${cache.get(argsString)}`);
        
        return cache.get(argsString);
      }
      const result = Reflect.apply(target, undefined, args);
      cache.set(argsString, result);

      return result;
    }
  })
}

const getFibProxy = getCacheProxy(getFib);
getFibProxy(40); // 102334155
getFibProxy(40); // 输出40的缓存结果: 102334155

在实际应用中数据量越大、计算过程越复杂,优化效果越好,否则有可能会得不偿失。

实现私有属性 ↑
场景:众所周知,JavaScript是没有私有属性这一个概念的,私有属性一般是以 下划线开头,请通过 Proxy 限制以 开头的属性的访问。

const myObj = {
  public: 'hello',
  _private: 'secret',
  method: function () {
    console.log(this._private);
  }
},

思路:看上去比较简单,貌似使用 get、set 这两个 trap 就可以,但实际上并不是。实际上还需要实现 has, ownKeys , getOwnPropertyDescriptor 这些 trap,这样就能最大限度的限制私有属性的访问。

代码:

function getPrivateProps(obj, filterFunc) {
  return new Proxy(obj, {
    get(obj, prop) {
      if (!filterFunc(prop)) {
        let value = Reflect.get(obj, prop);
        // 如果是方法, 将this指向修改原对象
        if (typeof value === 'function') {
          value = value.bind(obj);
        }
        return value;
      }
    },
    set(obj, prop, value) {
      if (filterFunc(prop)) {
        throw new TypeError(`Can't set property "${prop}"`);
      }
      return Reflect.set(obj, prop, value);
    },
    has(obj, prop) {
      return filterFunc(prop) ? false : Reflect.has(obj, prop);
    },
    ownKeys(obj) {
      return Reflect.ownKeys(obj).filter(prop => !filterFunc(prop));
    },
    getOwnPropertyDescriptor(obj, prop) {
      return filterFunc(prop) ? undefined : Reflect.getOwnPropertyDescriptor(obj, prop);
    }
  });
}

function propFilter(prop) {
  return prop.indexOf('_') === 0;
}

myProxy = getPrivateProps(myObj, propFilter);

console.log(JSON.stringify(myProxy)); // {"public":"hello"}
console.log(myProxy._private); // undefined
console.log('_private' in myProxy); // false
console.log(Object.keys(myProxy)); // ["public", "method"]
for (let prop in myProxy) { console.log(prop); }    // public  method
myProxy._private = 1; // Uncaught TypeError: Can't set property "_private"

注意:其中在 get 方法的内部,我们有个判断,如果访问的是对象方法使将 this 指向被代理对象,这是在使用 Proxy 需要十分注意的,如果不这么做方法内部的 this 会指向 Proxy 代理。

一般来讲, set trap 都会默认触发 getOwnPropertyDescriptor 和 defineProperty

函数节流 ↑

场景:控制函数调用的频率.

const handler = () => console.log('Do something...');
document.addEventListener('scroll', handler);

思路:涉及到函数的调用,所以使用 apply trap 即可。

代码:

const createThrottleProxy = (fn, rate) => {
  let lastClick = Date.now() - rate;
  return new Proxy(fn, {
    apply(target, context, args) {
      if (Date.now() - lastClick >= rate) {
        fn.bind(target)(args);
        lastClick = Date.now();
      }
    }
  });
};

const handler = () => console.log('Do something...');
const handlerProxy = createThrottleProxy(handler, 1000);
document.addEventListener('scroll', handlerProxy);

同样需要注意使用 bind 绑定上下文,不过这里的示例使用了箭头函数,不用 bind 也没啥问题。

图片懒加载 ↑

场景:为了更好的用户体验,在加载图片的时候,使用 loading 占位图,等真正图片加载完毕之后再显示出来。原始的写法如下:

const img = new Image();
img.src = '/some/big/size/image.jpg';
document.body.appendChild(img);

思路:加载图片的时候,会读取 img.src 属性,我们使用 constructor trap 控制在创建的时候默认使用 loading 图,等加载完毕再将真实地址赋给 img;

代码:


 const IMG_LOAD = 'https://img.alicdn.com/tfs/TB11rDdclLoK1RjSZFuXXXn0XXa-300-300.png';

  const imageProxy = (loadingImg) => {
      return new Proxy(Image, {
          construct(target, args){
              const instance = Reflect.construct(target, args);
              instance.src = loadingImg;
              return instance;
          }
      });
  };

  const ImageProxy = imageProxy(IMG_LOAD);

  const createImageProxy = (realImg) =>{
      const img = new ImageProxy();
      const virtualImg = new Image();
      virtualImg.src = realImg;
      virtualImg.onload = () => {
          hasLoaded = true;
          img.src = realImg;
      };
      return img;
  }
  var img = createImageProxy('https://cdn.dribbble.com/users/329207/screenshots/5289734/bemocs_db_dribbble_03_gold_leaf.jpg');
  document.body.appendChild(img);

监听属性更改 ↑

场景:当普通对象属性更改后,触发所绑定的 onChange 回调;

思路:能更改属性的有 set 和 deleteProperty 这两个 trap,在其中调用 onChange 方法即可


function trackChange(obj, onChange) {
    const handler = {
        set (obj, prop, value) {
            const oldVal = obj[prop];
            Reflect.set(obj, prop, value);
            onChange(obj, prop, oldVal, value);
        },
        deleteProperty (obj, prop) {
            const oldVal = obj[prop];
            Reflect.deleteProperty(obj, prop);
            onChange(obj, prop, oldVal, undefined);
        }
    };
    return new Proxy(obj, handler);
}

// 应用在对象上
let myObj = trackChange({a: 1, b: 2}, function (obj, prop, oldVal, newVal) {
    console.log(`myObj.${prop} changed from ${oldVal} to ${newVal}`);
});

myObj.a = 5;     // myObj.a changed from 1 to 5
delete myObj.b;  // myObj.b changed from 2 to undefined
myObj.c = 6;     // myObj.c changed from undefined to 6

// 应用在数组上
let myArr = trackChange([1,2,3], function (obj, prop, oldVal, newVal) {
    let propFormat = isNaN(parseInt(prop)) ? `.${prop}` : `[${prop}]`,
        arraySum = myArr.reduce((a,b) => a + b);
    console.log(`myArr${propFormat} changed from ${oldVal} to ${newVal}`);
    console.log(`  sum [${myArr}] = ${arraySum}`);
});

myArr[0] = 4;      // myArr[0] changed from 1 to 4         
                   //   sum [4,2,3] = 9
delete myArr[2];   // myArr[2] changed from 3 to undefined                
                   //   sum [4,2,] = 6
myArr.length = 1;  // myArr.length changed from 3 to 1                
                   //   sum [4] = 4

实现单例模式 ↑

场景:实现单例设计模式;

思路:和创建有关的,是 construct 这个 trap,每次我们返回相同的实例即可。

代码:


// makes a singleton proxy for a constructor function
function makeSingleton(func) {
    let instance,
        handler = {
            construct: function (target, args) {
                if (!instance) {
                    instance = new func();
                }
                return instance;
            }
        };
    return new Proxy(func, handler);
}


// 以这个为 constructor 为例
function Test() {
    this.value = 0;
}

// 普通创建实例
const t1 = new Test(),
    t2 = new Test();
t1.value = 123;
console.log('Normal:', t2.value);  // 0 - 因为 t1、t2 是不同的实例

// 使用 Proxy 来 trap 构造函数, 完成单例模式
const TestSingleton = makeSingleton(Test),
    s1 = new TestSingleton(),
    s2 = new TestSingleton();
s1.value = 123;
console.log('Singleton:', s2.value);  // 123 - 现在 s1、s2 是相同的实例。

像 Python 那样截取数组 ↑

场景:在 python 中,你可以使用 list[10:20:3] 来获取 10 到 20 索性中每隔 3 个的元素组成的数组(也支持负数索引)。

思路:由于在 JS 中,数组方括号语法中不支持冒号,只能曲线救国,使用这样 list["10:20:3"] 的形式。只需要实现 get trap 即可。


// Python-like array slicing

function pythonIndex(array) {

    function parse(value, defaultValue, resolveNegative) {
        if (value === undefined || isNaN(value)) {
            value = defaultValue;
        } else if (resolveNegative && value < 0) {
            value += array.length;
        }
        return value;
    }
    
    function slice(prop) {
        if (typeof prop === 'string' && prop.match(/^[+-\d:]+$/)) {
            // no ':', return a single item
            if (prop.indexOf(':') === -1) {
                let index = parse(parseInt(prop, 10), 0, true);
                console.log(prop, '\t\t', array[index]);
                return array[index];
            }                
            // otherwise: parse the slice string
            let [start, end, step] = prop.split(':').map(part => parseInt(part, 10));
            step = parse(step, 1, false);
            if (step === 0) {
                throw new RangeError('Step can\'t be zero');
            }
            if (step > 0) {
                start = parse(start, 0, true);
                end = parse(end, array.length, true);
            } else {
                start = parse(start, array.length - 1, true);
                end = parse(end, -1, true);
            }
            // slicing
            let result = [];
            for (let i = start; start <= end ? i < end : i > end; i += step) {
                result.push(array[i]);
            }
            console.log(prop, '\t', JSON.stringify(result));
            return result;
        }
    }

    const handler = {
        get (arr, prop) {
            return slice(prop) || Reflect.get(array, prop);
        }
    };
    return new Proxy(array, handler);
}


// try it out
let values = [0,1,2,3,4,5,6,7,8,9],
    pyValues = pythonIndex(values);

console.log(JSON.stringify(values));

pyValues['-1'];      // 9
pyValues['0:3'];     // [0,1,2]    
pyValues['8:5:-1'];  // [8,7,6]
pyValues['-8::-1'];  // [2,1,0]
pyValues['::-1'];    // [9,8,7,6,5,4,3,2,1,0]
pyValues['4::2'];    // [4,6,8]

// 不影响正常的索引
pyValues[3];         // 3

这里推荐一下我的前端学习交流群:731771211,里面都是学习前端的,如果你想制作酷炫的网页,想学习知识。欢迎新手,进阶的朋友。进群就送最新的web前端学习资料和0基础入门教程,欢迎初学和进阶中的小伙伴。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JavaScript—ES6 元编程(5) 的相关文章

  • Javascript 3d 绘图实用程序? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 有谁知道有什么好的 javascript 3d 绘图实用程序吗 我知道每个网站都推荐过画布 3d 图
  • 如何在 Python for 循环中获取 GAE ndb 中当前记录的密钥?

    我目前有一个网页 其中显示数据存储中的记录列表以及编辑链接 我想从数据库转换它 至新开发银行 我是 Python 和 GAE 新手 当前代码 tbody for listtype in listtypes tr td listtype Li
  • 在 Jest 测试中设置时刻时区

    我有 util 函数 它以特定的日期格式解析给定的日期 即 2019 01 28 然后使用momentJS检索当天的开始并将其转换为 ISO 日期格式 dates js import moment from moment export co
  • Firestore——仅获取大型同步集合中已更改的文档

    我已阅读下面的所有问题 但在文档中找不到任何内容来描述如何同步集合和接收only更改集合中的文档 我的同步集合中有超过 500 个文档 使用redux saga firebase 同步集合 https redux saga firebase
  • 为什么发送 fetch() 时我的响应数据未定义?

    我正在尝试在客户端使用 fetch 将数据发布到我的 NodeJS 服务器或从我的 NodeJS 服务器发布数据 服务器很好地收到了 post 请求 我能够记录 req 变量 但是当我 res send any data 时 客户端无法检测
  • EmberJS:如何为 ember-data RESTAdapter 中的模型提供特定的 URL?

    问题一 如果我有一个名为 Company 的余烬数据模型 我如何告诉它点击 businesses and businesses id而是检索记录 有没有办法指定给定模型的 url 更好的是 像 BackboneJS 一样 我可以在运行时计算
  • 如何从 WinRT StreamSocket 读取所有可用数据并清空 inputStream?

    我想在向套接字写入新数据之前读取当前正在等待套接字的所有数据 WinRT中的读取方法都是异步的 所以我不能简单地while直到套接字为空 由于我确实想丢弃套接字上的数据 因此我不想使用读取器 而是直接从套接字读取数据IInputStream
  • 如何在Python中获取套接字的外部IP?

    当我打电话时socket getsockname 在套接字对象上 它返回我的机器的内部 IP 和端口的元组 但是 我想找回我的外部IP 最便宜 最有效的方式是什么 如果没有外部服务器的配合 这是不可能的 因为您和另一台计算机之间可能存在任意
  • 使用XMLHttpRequest自动网页刷新内存泄漏

    问候 我一直在为一些使用 8 位微控制器的硬件开发网络界面 该网页使用 HTML javascript JSON 和 XHR XMLHttpRequest 进行通信 我想做的是创建一个页面 使用 setInterval 使用控制器中的新值每
  • Javascript 警报/消息框中的欧元符号或其他实体

    有谁知道我如何在 javascript 警报窗口中显示欧元或其他 html 实体 alert u20AC HTML 实体字符查找 http leftlogic com lounge articles entity lookup
  • 检测反射 DLL 注入

    在过去的几年中 恶意软件 以及一些渗透测试工具 如 Metasploit 的 meterpreter 负载 已经开始使用反射 DLL 注入 PDF http www harmonysecurity com files HS P005 Ref
  • Django admin.py 未知命令:'collectstatic'

    我已经从 django 1 2 7 升级到 django 1 5 1我正在使用 python 2 6 6当我尝试跑步时python manage py collectstatic i get 未知命令 collectstatic 从我的设置
  • django 南迁移,不设置默认值

    我使用 South 来迁移我的 Django 模型 然而 南方有一个令人讨厌的错误 它不会在 Postgres 数据库中设置默认值 例子 created at models DateTimeField default datetime no
  • NodeJS 错误堆栈未定义 [关闭]

    Closed 这个问题需要细节或清晰度 help closed questions 目前不接受答案 我正在使用节点检查器 我注意到new Error 有未定义的堆栈 如果我将此值分配给一个变量 该变量将显示堆栈未定义 有趣的是 跑步new
  • 出于安全目的,您是否有理由不执行自己的算法来打乱 ID?

    我计划实现我自己的非常简单的 哈希 公式 为具有多个用户的应用程序添加一层安全性 我目前的计划如下 用户创建一个帐户 此时后端会生成一个 ID ID 通过公式运行 假设 ID 57 8926 36 7 或同样随机的东西 然后 我将新的用户
  • 将索引数组转换为 NumPy 中的 one-hot 编码数组

    给定一个一维索引数组 a array 1 0 3 我想将其一次性编码为二维数组 b array 0 1 0 0 1 0 0 0 0 0 0 1 创建归零数组b有足够的列 即a max 1 然后 对于每一行i 设置a i 第 列 至1 gt
  • Spark (Python) 中的 Kolmogorov Smirnov 测试不起作用?

    我正在 Python Spark ml 中进行正态性测试 看到了我的结果think是一个错误 这是设置 我有一个标准化的数据集 范围 1 到 1 当我做直方图时 我可以清楚地看到数据不正常 gt gt gt prices norm hist
  • 如何始终将焦点保持在画布上?

    我一直在这个论坛寻找解决方案 但尚未找到 无论我在页面上的哪个位置单击 我都需要始终将焦点放在画布元素上 我有几个按钮 在每个 onclick 事件中我写 document getElementById canvas focus 这确实有效
  • ReactJS setState 仅在嵌套在 setState 中时才有效

    问题 当我使用 this setState 并在回调中输出状态时 它根本不会改变 但是当我将 setstate 嵌套在 setstate 中时 它将正常工作 例子 这不行 this setState data newData 这确实有效 t
  • matplotlib imshow() 和像素强度

    我试图了解矩阵的值是如何输入到 matplotlib 的imshow 函数确定灰度模式下像素的强度 考虑示例代码 import random import matplotlib pyplot as plt import matplotlib

随机推荐

  • msvcr120.dll丢失的解决方法,win10系统dll报错解决方法

    今天 我将和大家分享一个在win10系统中经常遇到的问题 msvcr120 dll丢失 相信很多朋友在使用电脑的过程中都会遇到这个问题 那么如何解决呢 接下来 就让我为大家详细讲解一下解决方法 首先 我们来了解一下msvcr120 dll文
  • Kazantzis-Kravaris-Luenberger ( KKL )非线性观测器

    The formulation of the observer design problem is realized via a system of singular first order linear PDEs and a rather
  • 【Python搞搞轻量Blog】第二发 Flask入门(2)

    上一篇文章 Flask入门 中创建了一个非常简单的Web应用 但从最简单中又稍微深入的说了一下Flask 基于Werkzeug 使用模板 利用HTML 如何快速利用写好的HTML呢 第一步 在和主应用的同级目录下 创建一个名为 templa
  • [算法]PRML学习笔记1.2.6贝叶斯曲线拟合(Bayesian curve fitting)

    参考文献 Pattern Recognition and Machine Learning Published by Springer January 2006 https www microsoft com en us research
  • 【Vue 学习】修改页面默认的标题

    修改页面默认的标题 一般是在 public index html 里面进行设置网页标题 如下 Vue 的脚手架使用了读取配置文件的方式来设置网页标题 如下 配置文件的内容如下 总结 直接修改 public index html 文件 修改配
  • FPN(特征金字塔)图解

    FPN 看了mask RCNN的源代码 其中用到fpn 根据源代码画了个图 当然FPN重要的是它的思想 有一些超参数可以根据自己的框架进行调整
  • [HFSS]Floquet port激励及主从边界设置(实例)

    Floquet port激励及主从边界设置 1 Floquet port简介 2 基本模型建立 3 主从边界设置 4 wave port 设置 5 floquet port 设置 6 Analysis setup 7 验证 仿真 8 结果演
  • Python标准库、模块、包的区别

    文章目录 模块 包 标准库 第三方库 自定义模块 模块 模块可定义为一个包含python定义和语句的 py文件 模块中包含python代码以及python函数 类或python变量 一个模块可以被其他 py文件导入使用 也可以单独作为脚本文
  • 3.3 Git 分支 - 分支管理

    3 3 Git 分支 分支管理 版本说明 版本 作者 日期 备注 0 1 loon 2019 3 23 初稿 目录 文章目录 3 3 Git 分支 分支管理 版本说明 目录 分支管理 分支管理 现在已经创建 合并 删除了一些分支 让我们看看
  • 51单片机的几种中断的用法

    1 外部中断0 外部中断0实验 实现现象 下载程序后按下K3按键可以对D1小灯状态取反 注意事项 无 include reg52 h 此文件中定义了单片机的一些特殊功能寄存器 typed
  • gitlab可以访问,但git clone连接超时不能下载

    因此 我们可以把 http wozhendetainanle map 地址后面加上 git后缀 更改为 http wozhendetainanle map git 此时使用就可以下载了
  • 关于C#监视剪贴板信息

    1 常规方法 在C 中 有一个常规检测剪贴板的方法 用的是 System Windows Forms Clipboard 使用起来很简单 代码如下
  • 微信小程序中的App、Page、Component的生命周期函数

    有点混乱的官方文档 一 什么是生命周期和生命周期函数 字面意义上说 生命周期就是指一个对象自身的生老病死 在程序运行上也可以这么理解 程序也是对象 也有 生老病死 程序自身从创建到销毁的过程中 运行到特定的阶段 会触发特定的函数 这些函数
  • Distributed Database System —— 什么是嵌入式数据库?

    文章目录 什么是嵌入式数据库 Embedded Database 数据库服务器的架构 嵌入式数据库架构 区别 像Oracle Sybase MySQL和SQL Server这些大家熟知的数据库都属于数据库服务器 当然不排除某些也提供嵌入式版
  • “新”科学家Stephen Wolfram

    2011年10月的iPhone 4S发布会上 苹果副总裁Scott Forstall长按Home键 对着那个泛着紫色光晕的话筒问道 Stephen Wolfram 离圣诞节还有几天 让我查查 稍等 Scott得到了他想要的答案 82 天 也
  • 单片机设计_室内环境智能监测系统(STM32 OLED ESP8266 DHT11 MQ-2 加湿器)

    想要更多项目私wo 一 电路设计 室内环境智能监测系统 主要功能 1 检测空气温湿度 2 检测光照强度 3 检测烟雾浓度 4 数据显示在手机端和设备OLED屏幕上 5 当空气温度高于设定的阈值时 风扇开启 6 当空气湿度低于设定的阈值时 加
  • Hive基本架构和原理

    概述 Hive是建立在 Hadoop 上的数据仓库基础构架 它提供了一系列的工具 可以用来进行数据提取转化加载 ETL 这是一种可以存储 查询和分析存储在 Hadoop 中的大规模数据的机制 Hive 定义了简单的类 SQL 查询语言 称为
  • implicit declaration of function 问题解决

    C语言程序编译后出现警告 warning implicit declaration of function client tcpinit Wimplicit function declaration 原因 相关的头文件没有声明这个函数 在相
  • 如何使用Java以编程方式在Excel中创建数据透视表?

    Excel电子表格中的数据透视表用于以交互方式汇总数据 假设在工作表中有许多发票的数据 在这种情况下 可以使用数据透视表汇总按客户或产品分组的发票 在本文中 将学习如何以编程方式处理Excel中的数据透视表 特别是 将了解如何在Java中创
  • JavaScript—ES6 元编程(5)

    几年前 ES6 刚出来的时候接触过 元编程 Metaprogramming 的概念 不过当时还没有深究 在应用和学习中不断接触到这概念 比如 mobx 5 中就用到了 Proxy 重写了 Observable 对象 觉得有必要梳理总结一下