杜建军 发布的文章

面试的时候被问到“平常用闭包都做过哪些事情?”

闭包的概念

《你不知道的JavaScript(上卷)》

当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行

关于闭包的概念,仅此一句就够了。

解读闭包

正常情况下,当词法作用域内的代码执行完毕后,在这个词法作用域内定义的变量都会被回收掉。
不正常的情况下,也就是有闭包的时候。这个词法作用域内定义的变量“可以被记住”,而不会被回收掉。记住这些变量的是一个函数。

function P(){
    let a = "a"
    let b = "b"
    return function innerFunc(){
        return a+b
    }
}
// 在作用域外部访问到了P作用域内部定义的ab变量
// ab 
P()()

在上面的例子中,innerFunc “记住了”所在的词法作用域(a和b变量),产生了闭包。
innerFunc 函数在 P 的作用域外部执行时,也能访问到内部的词法作用域。

使用闭包的例子

1.模拟定义一个对象的私有属性

const P = (function (){
    let _gender = ""
    function People(name,gender){
        this.name = name
        _gender = gender
    }
    People.prototype.getGender = function (){
        return _gender
    }
    return People
})()

const people = new P("小明","male")
//  { name: '小明' }
console.log(people)
// male
console.log(people.getGender())

2.给标签添加事件

有作用域的地方就有闭包

const container = [{},{},{}]

for (let i = 0;i<container.length;i++){
    container[i].run = function () {
        console.log("当前是第"+ ++i +"个容器")
    }
}
// 当前是第1个容器
container[0].run()
// 当前是第3个容器
container[2].run()

用 let 声明 i 变量,循环体运行了3次,一共就创建了3个块级作用域。然后通过三个匿名函数“记住了”这3个块级作用域(i变量 ),创建了3个闭包。

vue3官方的中文文档还没出来。这几天在看vue3官方英文的文档。其实直接读英文文档是比较困难的,多次阅读才能对其表达的意思理解。那么,就从第三篇开始翻译吧。

前提:没有直译,并且认为直译是很 low 的做法。按照自己的理解组织语言,难免会有错误的地方。望不吝赐教。

原文 : https://v3.vuejs.org/guide/instance.htm

应用实例和组件实例

创建应用实例

每个 Vue 应用都起源于一个 vue 应用实例。可以通过 Vue 库的 createApp 方法创建一个vue应用实例。

const app = Vue.createApp({ /* options */ })

vue 应用实例会被注册到全局,供此应用内部的组件去调用它。

const app = Vue.createApp({})
app.component('SearchInput', SearchInputComponent)
app.directive('focus', FocusDirective)
app.use(LocalePlugin)

vue应用实例暴露的很多接口都返回其自身,这让我们可以链式地组织代码。

Vue.createApp({})
  .component('SearchInput', SearchInputComponent)
  .directive('focus', FocusDirective)
  .use(LocalePlugin)

根组件

可以给 createApp 传入一个参数来配置根组件,根组件是渲染的起点。

vue应用实例必须挂载于一个 Dom 节点上才能生效。比如我们想把应用实例挂载在<div id="app"></div> 上,可以给 mount 方法传入 #app

const RootComponent = { /* options */ }
const app = Vue.createApp(RootComponent)
const vm = app.mount('#app')

和vue应用实例的大多数方法不一样,mount 方法不会返回vue应用实例本身,而是返回根组件实例。

vue 在一定程度上受到 mvvm 模式的启发,所以我们一般约定使用 vm(ViewModel的简写) 标识符表示组件实例。

上面例子中,我们写页面只用了一个组件。其实大多数的真实场景是:一个vue应用由许多可复用的组件组成了一个树状的结构。比如 Todo 应用可能会是这种结构:

Root Component
└─ TodoList
   ├─ TodoItem
   │  ├─ DeleteTodoButton
   │  └─ EditTodoButton
   └─ TodoListFooter
      ├─ ClearTodosButton
      └─ TodoListStatistics

所有的组件都有其自己的实例---vm。对于像 Tode 这种,一次渲染多个组件的应用,在当前应用中,所有的组件都共享同一个应用实例。

后面会详细的介绍组件系统。现在你只需要知道:根组件和其他组件并没有太大区别。配置选项与相应组件实例相同。

组件实例的属性

在手册的开头部分,我们就邂逅了 data 属性。在 data 中定义的属性通过组件实例暴露了出来。

const app = Vue.createApp({
  data() {
    return { count: 4 }
  }
})

const vm = app.mount('#app')

console.log(vm.count) // => 4

在组件实例上,存在很多选项供用户使用,比如methods, props, computed, injectsetup 。这些内容会在手册后面详细介绍。在组件实例上定义的所有属性,不管是通过什么方式定义的,都可以在组件模板上直接使用。

Vue 暴露了一些组件实例的内部属性,如$attrs$emit。为避免内部属性和用户定义的属性产生冲突。内部属性的前缀都是 $

生命周期钩子

所有组件实例创建后都需要经历一系列的初始化过程,比如初始化“数据监听”,编译模板,给DOM上挂载,数据变化时更新DOM。在这个过程中可以运行一些生命周期钩子函数。让用户可以在特定的阶段运行自定义的代码。

比如 created钩子,可以在组件创建完成(created)后 运行用户自定义的代码。

Vue.createApp({
  data() {
    return { count: 1 }
  },
  created() {
    // `this` points to the vm instance
    console.log('count is: ' + this.count) // => "count is: 1"
  }
})

还有一些其他的钩子在组件实例生命周期的不同阶段运行,如 mounted,updated,unmounted。这些钩子的this指向当前组件实例的上下文。

注意:
不要使用箭头函数,因为箭头函数本身没有 this,所以在箭头函数中,会把this当作一个变量来对待,在其作用域链上查找 this。

生命周期图

下面就是组件实例的生命周期,慢慢去理解吧。
image

如果你使用百度搜索“使用 python 运行 js 程序”之类的关键字,得到的结果无非是"pyv8","pyexecjs,","js2py","直接操作node"。
那么很遗憾,以上四种方案都很不ok。

我花费很长时间,查阅了大量资料,总结出一个道理:如果在百度里很长时间都找不到的答案,不妨去 Google 一下,使用英文关键字。

这里介绍的是 PyMiniRacer ,它是一个很棒的库,可以完美替代谷歌的 pyv8 ,让你在任何版本的Python里都能通过 pip 很轻松的安装。

PyMiniRacer 仓库

特点:轻,安装方便,使用v8引擎(快),可以记录上下文环境,项目在积极维护中

安装:pip install py_mini_racer
使用:(更多用法见github)

from py_mini_racer import py_mini_racer
ctx = py_mini_racer.MiniRacer()
ctx.eval("""
function escramble_758(){
    var a,b,c
    a='+1 '
    b='84-'
    a+='425-'
    b+='7450'
    c='9'
    return a+c+b;
}
""")
ctx.call("escramble_758")

分别介绍一下其他库的缺点:

pyv8

2013 年开始就不维护了,支持python2 和早期python3 ,对python版本有要求,安装难,安装了不一定能用。

pyexecjs

这个也不维护了,作者的话:“pyexecjs性能差,推荐大家去用性能更好的pyv8”
pyexecjs 本身依赖js运行环境.首先在python中调用pyexecjs,然后pyexecjs再去启动nodejs。性能差

js2py

这个库的核心是“将js 代码翻译成 py”,自然会缺失很多js的系统函数,最后出现很多奇怪的bug

dukpy

这个库是基于 duktape 引擎的,不支持 es6。可能会出现很多奇怪的bug


最后,可以在 https://stackoverflow.com/questions/10136319/executing-javascript-from-python 发现更多的 “在 python 中运行js”的答案

每次都百度,不如总结一起。方便复制粘贴

1. python 豆瓣源

 -i http://pypi.douban.com/simple/ --trusted-host=pypi.douban.com/simple

2. ubuntu 20 源

deb https://mirrors.ustc.edu.cn/ubuntu/ focal main restricted universe multiverse
deb-src https://mirrors.ustc.edu.cn/ubuntu/ focal main restricted universe multiverse
deb https://mirrors.ustc.edu.cn/ubuntu/ focal-updates main restricted universe multiverse
deb-src https://mirrors.ustc.edu.cn/ubuntu/ focal-updates main restricted universe multiverse
deb https://mirrors.ustc.edu.cn/ubuntu/ focal-backports main restricted universe multiverse
deb-src https://mirrors.ustc.edu.cn/ubuntu/ focal-backports main restricted universe multiverse
deb https://mirrors.ustc.edu.cn/ubuntu/ focal-security main restricted universe multiverse
deb-src https://mirrors.ustc.edu.cn/ubuntu/ focal-security main restricted universe multiverse
deb https://mirrors.ustc.edu.cn/ubuntu/ focal-proposed main restricted universe multiverse
deb-src https://mirrors.ustc.edu.cn/ubuntu/ focal-proposed main restricted universe multiverse

3. selenium

chrome 下载

wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
sudo dpkg -i google-chrome*.deb
google-chrome --version

chromedriver下载路径:
http://npm.taobao.org/mirrors/chromedriver/

4. composer 阿里云镜像

composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/

5. npm

5.1. cnpm(淘宝)

npm install -g cnpm --registry=https://registry.npm.taobao.org

5.2. 配置国内源

npm config set registry https://registry.npm.taobao.org

5.3. yarn

yarn config set registry https://registry.npm.taobao.org/

6. github

6.1 访问加速 (git clone xxx)

将github.com 的域名替换为 https://github.com.cnpmjs.org 即可,后面的路径不要更改。
或者用 https://gitclone.com/

6.2 下载加速 (releases)

https://gh.msx.workers.dev/
将下载链接填进去即可,复制加速后的链接,也可以在服务器上用wget 下载。

不会吧,不会吧。都21世纪了,还有不会用 composer 的 phper?

1. 创建composer.json文件

{
    "autoload":{
        "psr4":{},
        "psr0":{},
        "files":[],
        "classmap":[]
    }
}

自动加载文件的大致架构如上。

2.psr4加载

遵循psr4规范加载,不用在文件目录中体现命名空间。composer.json 格式如下

{
    "autoload": {
    "psr-4": {
      "App\\": "app"
    }
}
app\DemoPsr4
<?php

namespace App;
class DemoPsr4
{
    public function __construct()
    {
        echo "psr4加载";
    }

}
public/index.php
<?php
require "../vendor/autoload.php";

// psr4 加载。
$p4 = new App\TestPsr4();

3. 生成自动加载文件

在运行测试前,要使用composer命令生成自动加载文件

composer dumpautoload

现在运行 index.php 。即可输出 “psr4加载” 字样

4. psr0 加载

当使用psr0规范时,需要在文件目录中创建一个和‘命名空间’名称相同的目录

composer.json
{
  "autoload": {
    "psr-4": {
      "App\\": "app"
    },
    "psr-0": {
      "Bpp\\": "bpp"
    }
  }
}
bpp/Bpp/DemoPsr0.php
<?php

namespace Bpp;
class DemoPsr0
{
    public function __construct()
    {
        echo "psr0自动加载。";
    }
}
public/index.php
<?php
require "../vendor/autoload.php";

$p4 = new App\TestPsr4();
$p0 = new Bpp\TestPsr0();

运行 composer dumpautoload 生成自动加载文件。
然后运行public/index.php,会发现 psr0 类中的输出语句执行了

5.classmap

classmap 后面是个数组,写一个目录进去。会扫描这个目录下所有的文件。生成一个 类与文件名对应的数组。可以不用遵循任何标准,可以不写命名空间,甚至类名和文件名不相同都行。

具体文件见文件 “classmap”文件夹下

6.files

上面方法都是加载‘类文件’,如果需要加载一个php文件,这个php文件里写的是函数。就要使用 files 字段了。

详细代码见文件,'helper.php'

代码地址:https://github.com/jianwi/composer_autoload.git