陈晨的博客

Talk is cheap. Show me the code.


  • Home

  • menu.list

跨端开发痛点?送你一款Vue最流行的跨端框架——uni-app

Posted on 2019-09-26

前言

今天来聊一下前端必备技能——小程序开发。

从最早发布的微信小程序,到后来的支付宝小程序、字节跳动小程序、百度小程序、QQ小程序,还有最近发布的360小程序,面对这么多套的代码,开发者该如何开发呢?

当业务要求同时在不同的端都要展现时候,针对不同的端去编写多套代码的成本显然非常高。这时候只需编写一套代码,就能够适配多端的能力就显得尤为需要。

今天就来给大家介绍一款,使用Vue的跨端框架——uni-app

uni-app 框架简介

uni-app 是一个使用 Vue.js 开发跨平台应用的前端框架,可编译到 iOS、Android、H5、以及各种小程序(微信/支付宝/百度/头条/QQ/钉钉)等平台。

uni-app 在开发者数量、案例、跨端抹平度、扩展灵活性、性能体验、周边生态、学习成本、开发成本等 6 大关键指标上拥有极强的竞争优势:

  • 开发者/案例数量更多
  • 跨端抹平度/扩展灵活性(通过条件编译,调用专有能力而不影响其他平台)
  • 性能体验更优秀(App 端支持 weex 渲染,带来更流畅的用户体验。)
  • 周边生态丰富(推出插件市场,能够提供较多的组件和模板,真正做到开箱即用)
  • 学习成本低(采用Vue.js 语法 + 小程序 API)
  • 开发成本低(不止开发成本,招聘、管理、测试各方面成本都大幅下降。)

虽然不同端框架环境千变万化,无论各类小程序、Weex、React-Native、Flutter、快应用,它们万变不离其宗的是MVVM架构设计思想。uni-app希望既能用一套代码完成所有端需求,将相同的业务逻辑完成收敛到同一层系统里面,又不会因为项目的抽象一致导致可维护性变差。

开发工具

通过 HBuilderX 可视化界面, HBuilderX 内置相关环境,开箱即用,无需配置 nodejs。

开发语言

使用Vue.js + 小程序 API,进行开发。

  • 数据绑定及事件处理同 Vue.js 规范,同时补充了 App 及页面的生命周期。

  • uni-app 帮我们把不同平台的小程序API 几乎都封装了 ,只需要将前缀替换为 uni 即可 举例说明:

在微信中发送 request 请求:

1
2
3
4
5
6
wx.request({
url: "https://www.example.com/request", //仅为示例,并非真实接口地址
success: res => {
console.log(res.data);
}
});

在 uni-app中发送 request 请求:

1
2
3
4
5
6
uni.request({
url: "https://www.example.com/request", //仅为示例,并非真实接口地址
success: res => {
console.log(res.data);
}
});

了解微信小程序的你,是不是感觉很简单呢?无非就是把 wx 前缀 换成了 uni 而已,而且uni.request是全端通用的 ajax 请求 API 哦,最大程度的降低开发者的学习成本。

如何解决跨端兼容问题

每个平台有自己的一些特性,因此会存在一些无法跨平台的情况。

uni-app 提供了条件编译手段,在一个工程里优雅的完成了平台个性化实现。

1
2
3
4
5
6
7
8
<view class="content">
<! -- #ifdef MP-WEIXIN -->
<view>只会编译到微信小程序</view>
<! -- #endif -->
<! -- #ifdef APP-PLUS -->
<view>只会编译到app</view>
<! -- #endif -->
</view>

nvue 模式

uni-app 里根据编译配置不同,可以使用 weex 的组件,也可以使用小程序组件(即uni-app组件)。编写页面时页面后缀名为 .nvue (native vue 的缩写)

uni-app App 端内置 weex 渲染引擎,提供了原生渲染能力,让uni-app在App端有更好的表现。

nvue 相当于给 weex 补充了大量 uni-app 的组件和 api,以及丰富的 Plus API、Native.js、原生插件。了解更多 nvue

官方 ui 库,uni-ui

uni-ui是DCloud提供的一个跨端ui库,它是基于vue组件的、flex 布局的、无 dom 的跨全端 ui 库。

web 开发中有的开发者习惯用一个ui库完成所有开发,但在uni-app体系中,推荐开发者首先使用性能更高的基础组件,然后按需引入必要的扩展组件。GitHub 地址:uni-ui

看到这里你是不是觉得特别心动了呢?别着急,让我们看一下uni-app的开发规范。

uni-app 开发规范

为了实现多端兼容,综合考虑编译速度、运行性能等因素,uni-app 约定了如下开发规范:

  • 页面文件遵循 Vue 单文件组件 (SFC) 规范
  • 组件标签靠近小程序规范,详见uni-app 组件规范
  • 接口能力(JS API)靠近微信小程序规范,但需将前缀 wx 替换为 uni,详见uni-app接口规范
  • 数据绑定及事件处理同 Vue.js 规范,同时补充了 App 及页面的生命周期
  • 为兼容多端运行,建议使用 flex 布局进行开发

关于各端的管理规则需要耐心学习

uni-app 并不难学,但我们注意到很多新人在适应各个平台的规则限制时很焦躁。

uni-app 跨了很多端,虽然代码层面可以开发一次,生成多端。但注意每个端,有每个端的管理规则,这与开发无关,但每个开发者仍然要耐心掌握这些规则限制。

  • 比如 H5 端的浏览器有跨域限制;
  • 比如微信小程序会强制要求 https 链接,并且所有要联网的服务器域名都要配到微信的白名单中;
  • 比如 App 端,iOS 对隐私控制和虚拟支付控制非常严格;
  • 比如 App 端,Android、国产 rom 各种兼容性差异,尤其是因为谷歌服务被墙,导致的 push、定位等开发混乱的坑;
  • 如果你的 App 要使用三方 sdk,比如定位、地图、支付、推送…还要遵循他们的规则和限制;

总结

uni-ui 对比其他框架主要有两个优势

  • 如何在有限前端团队人数下搞定更多平台,是需要首要考虑的原因,跨端方面uni-app更成熟。
  • 团队里熟悉vue技术栈的同学多一点,则使用uni-app内部培训时间短、风险低。

开发需要注意的点

  • 注意各端的差异性,很多东西,h5 是对的,上真机就错了,真机好着的,换小程序就错了,不同小程序之间还有差异,总之就是考虑好不同的情况,重点是仔细阅读文档。

  • 由于 uni-app 框架集成了5+API,一些需要原生功能 uniapp 也可以通过5+API去进行实现。

  • 很多的坑还是因为多端不兼容,除了写起来麻烦一点,基本上都还是有可以解决的策略。

关于 uni-app 的更多信息可以参考官方文档 uni-app - 多端开发框架 ,或者直接前往 GitHub 仓库 uni-app 查看 uni-app 文档及相关资料。欢迎前去 star 或是提 issue 哈。github地址:https://github.com/dcloudio/uni-app/

PS:我现在在写一个 ui 库,接下来我应该会写一些如何从零实现一个 ui 库方面的文章,欢迎有这方面经验的小伙伴们相互交流一下。

用localStorage存储购物车数据实战

Posted on 2019-06-10

最近做了一个商城项目,那肯定要做个购物车的嘛,于是我就想用localstorage存储商品,以便用户下次进入网页还可以看到自己收藏过的商品。业务方面,就保存商品数量、商品id和商品详情就好了。

接下来是项目实战

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 加购物车
let k = 0; // 定义一个参数,用在循环中计算是否有这个商品id,如果没有,就把商品添加到gifts中,如果有,那这个商品的num增加
let gift = {
id: this.giftDetail.goods_id,
gift: this.giftDetail,
num: this.num
};
let gifts = localStorage.getItem("gifts")
? JSON.parse(localStorage.getItem("gifts"))
: [];
for (let i = 0; i < gifts.length; i++) {
let item = gifts[i];
if (item.id === gift.id) {
item.num += gift.num;
} else {
k = k + 1;
}
}
if (k === gifts.length) {
gifts.push(gift);
}

localStorage.setItem("gifts", JSON.stringify(gifts));

注意点:

  1. 数组存localstorage是需要序列化的,否则会自动调用toString()方法。
  2. 需要用k来计算是否有这个商品是否存在。

vue项目首屏打开速度慢?我来带你飞。

Posted on 2019-03-30

最近接手了一个后台管理系统,技术栈主要是vue全家桶+elementui,老大打开测试环境页面的时候,说看到首页需要6秒钟,那如何进行优化呢?

首先我们需要安装webpack-bundle-analyzer

1
2
3
4
5
6
7
8
9
10
11
12
13
// webpack.prod.conf.js
if (config.build.bundleAnalyzerReport) {
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}
// config/index.js
build: {
// Run the build command with an extra argument to
// View the bundle analyzer report after build finishes:
// `npm run build --report`
// Set to `true` or `false` to always turn it on or off
bundleAnalyzerReport: process.env.npm_config_report
}

运行npm run build --report


我们可以看到,vendor中的elementui占了500k,怎么优化呢?

在webpack配置文件中增加,接下来就是见证奇迹的时刻。

1
2
3
4
5
externals: {
'vue': 'Vue',
'element-ui': 'ELEMENT',
'axios': 'axios'
},

再看一下我们的vendor体积


vendor一共才195k

那缺少的elementui文件去哪里找呢?答案是cdn引用。

之前项目里还有引用moment,但是这个库实在是太大了,在github上我找到一个跟momentapi完全一样的库,但是文件大小只要2kb。


其他优化方法还有ssr,这个最好用nuxtjs来做,自己配置ssr实在太麻烦了。

如果解决了你的问题,帮我点个赞吧

(已解决)mac连接手机热点,可以上微信,但不能打开网页

Posted on 2019-03-17

今天带电脑到外面学习,发现mac连接手机热点可以上微信,但不能打开网页,ping 百度也ping不通,找了半天网上的方法,解决方法如下:



设置到现在,就没法上网,只能聊微信。

接下来要设置dns了,设置好dns就可以正常上网了。


dns要设置跟路由器同样的ip。

至此,就可以上网了。

使用vue导出excel遇到的那些坑

Posted on 2019-03-14

需求:

Vue+element UI el-table下的导出当前所有数据到一个excel文件里。

先按照网上的方法,看看有哪些坑

准备工作:

1、安装依赖:yarn add xlsx file-saver -S

2、在放置需要导出功能的组件中引入
  

1
2
import FileSaver from "file-saver";
import XLSX from "xlsx";

3、HTML中的设置,简单来说就是给需要导出的table标签el-table上加一个id:如outTable,对应下面的exportExcel方法中的 document.querySelector(‘#outTable‘)

1
2
3
4
5
6
7
8
9
   //导出当前表格
exportCurrent:function(){
var wb = XLSX.utils.table_to_book(document.querySelector('#outTable')) //表格id
var wbout = XLSX.write(wb, { bookType: 'xlsx', bookSST: true, type: 'array' })
try {
FileSaver.saveAs(new Blob([wbout], { type: 'application/octet-stream' }), 'sheet.xlsx') //文件名
} catch (e) { if (typeof console !== 'undefined') console.log(e, wbout) }
return wbout
},

我们来看一下原始数据


接下来再来看一下导出的结果


哎???我订单编号跟银行卡号咋成了科学计数法了??

还有我的时间,时分秒呢??

原因是因为数字太长了,需要使用excel的文本格式才能显示正常

经过各种搜索,最终解决方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
exportExcel() {
var xlsxParam = { raw: true };//转换成excel时,使用原始的格式
var wb = XLSX.utils.table_to_book(document.querySelector("#outTable"),xlsxParam);
var wbout = XLSX.write(wb, {
bookType: "xlsx",
bookSST: true,
type: "array"
});
try {
FileSaver.saveAs(
new Blob([wbout], { type: "application/octet-stream;charset=utf-8" }),
"sheetjs.xlsx"
);
} catch (e) {
if (typeof console !== "undefined") console.log(e, wbout);
}
return wbout;
},

再来看我们的数据


大功告成,如果解决了你的问题的话,可以帮我点个赞吗,谢谢了。

面试必问之继承

Posted on 2019-02-05

js继承常用的几种方法,记录一下,马上要面试了。
觉得有用可以帮我点个赞吗?谢谢了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 原型链继承
function Parent() {
this.name = '原型链继承';
this.play = [1,2,3];
}
Parent.prototype.getName = function () {
console.log(this.name);
}

function Child() {
this.type = '原型链继承child';
}
Child.prototype = new Parent();
// 原型链上的原型对象是通用的,改变一个,其他的都会改变,但我们不想所有对象都改变
var child1 = new Child();
var child2 = new Child();
child1.play.push(4)
console.log(child1.play)
console.log(child2.play)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 构造函数继承
function Parent() {
this.name = '构造函数继承';
this.play = [1,2,3];
}
Parent.prototype.getName = function () {
console.log(this.name);
}

function Child() {
Parent.call(this)
this.type = '构造函数继承child';
}

var child1 = new Child();
console.log(child1.getName)
//构造函数继承不会继承原型链上的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 组合继承
// 原理:创建中间对象,中间对象的原型对象是父类的
function Parent() {
this.name = '组合继承';
this.play = [1,2,3];
}
Parent.prototype.getName = function () {
console.log(this.name);
}

function Child() {
Parent.call(this)
this.type = '组合继承child';
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
//没有这句代码,Child.prototype.constructor会指向Parent
var child = new Child()
console.log(child instanceof Child,child instanceof Parent);
console.log(child.constructor);

Mac环境下手把手教你如何使用mongoDB+Robo 3T

Posted on 2019-01-21

1.在Mac下安装MongoDB

用homebrew安装最简单,推荐使用此方法安装

1
sudo brew install mongodb

安装完成后,运行mongod会发现报错,别慌

报错原因是因为你没有data/db这个文件夹

所以执行以下代码

1
2
3
sudo mkdir -p /data/db

sudo chown ‘username’ /data/db

完成后,再执行mongod


大功告成

2.接下来安装 Robo 3T

官网:https://robomongo.org/download


接下来安装就好了

记住开软件连接数据库时,命令行一定要启动mongod

实现一个简易的vue

Posted on 2018-12-26

1./compiler ⽬目录是编译模版;

2./core ⽬目录是 Vue.js 的核⼼心(也是后⾯面的重点);

3./platforms ⽬目录是针对核⼼心模块的 ‘平台’ 模块;

4./server ⽬目录是处理理服务端渲染;

5./sfc ⽬目录处理理单⽂文件 .vue;

6./shared ⽬目录提供全局⽤用到的⼯工具函数。

Vue.js 的组成是由 core + 对应的 ‘平台’ 补充代码构成(独立构建和运行时构建 只是 platforms 下 web 平台的两种选择)。

vue的双向数据绑定

双向绑定(响应式原理)所涉及到的技术

1
2
3
4
5
1. Object.defineProperty
2. Observer
3. Watcher
4. Dep
5. Directive

1. Object.defineProperty

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var obj = {};
var a;
Object.defineProperty(obj,'a',{
get: function(){
console.log('get val');
return a;
},
set: function(newVal){
console.log('set val:' + newVal);
a = newVal;
}
});
obj.a // get val; 相当于<span>{{a}}</span>
obj.a = '111'; // set val:111 相当于<input v-model="a">


setter 触发消息到 Watcher watcher帮忙告诉 Directive 更新DOM,DOM中修改了数据 也会通知给 Watcher,watcher 帮忙修改数据。

2. Observer

1
2
3
4
5
6
7
观察者模式是软件设计模式的一种。
在此种模式中,一个目标对象管理所有相依于它的观 察者对象,并且在它本身的状态改变时主动发出通知。
这通常透过呼叫各观察者所提供的 方法来实现。此种模式通常被用来实时事件处理系统。
订阅者模式涉及三个对象:
发布者、主题对象、订阅者,三个对象间的是一对多的关系,
每当主题对象状态发生改变时,其相关依赖对象都会得到通知,并被自动更新。
看一个简单的示例:


vue里边怎么操作的呢? vue observer



  1. watcher

  1. Dep

  1. Directive


弄明白原理和架构之后,我们来实现一个简单的vue双向数据绑定

1.这个Vue是从哪里来的呢?


是通过上述方法实例化的一个对象;但是里边有两个未知生物 observe ? Compile?

observe中写的是双向绑定的核心原理就是Object.defineProperty

通过set,get来设置值与获取值

把text属性绑定到vue实例上面去使用

那其中的Dep又是什么呢?


添加订阅者跟通知订阅更新

再来看一下Compile中写的什么吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
function Compile(node, vm) {
if (node) {
this.$frag = this.nodeToFragment(node, vm);
return this.$frag;
}
}
Compile.prototype = {
nodeToFragment: function (node, vm) {
var self = this;
var frag = document.createDocumentFragment(); // 创建一段html文档片段
var child;

while (child = node.firstChild) {
self.compileElement(child, vm);
frag.append(child); // 将所有子节点添加到fragment中
}
return frag;
},
compileElement: function (node, vm) {
var reg = /\{\{(.*)\}\}/;

//节点类型为元素
if (node.nodeType === 1) {
var attr = node.attributes;
// 解析属性
for (var i = 0; i < attr.length; i++) {
if (attr[i].nodeName == 'v-model') {
var name = attr[i].nodeValue; // 获取v-model绑定的属性名
node.addEventListener('input', function (e) {
// 给相应的data属性赋值,进而触发该属性的set方法
// 触发set vm[name]
vm[name] = e.target.value;
});
// node.value = vm[name]; // 将data的值赋给该node
new Watcher(vm, node, name, 'value');
}
};
}
//节点类型为text
if (node.nodeType === 3) {
if (reg.test(node.nodeValue)) {
var name = RegExp.$1; // 获取匹配到的字符串
name = name.trim();
// node.nodeValue = vm[name]; // 将data的值赋给该node
new Watcher(vm, node, name, 'nodeValue');
}
}
},
}

哦,原来Compile中是渲染html的啊。其中的Watcher是不是监控节点变化,然后给Dep通知的呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function Watcher(vm, node, name, type) {
Dep.target = this;
this.name = name; //text
this.node = node; // 节点
this.vm = vm; // vue实例
this.type = type; //nodeValue 当前节点的值
this.update();
Dep.target = null;
}

Watcher.prototype = {
update: function() {
this.get();
var batcher = new Batcher();
batcher.push(this);
// this.node[this.type] = this.value; // 订阅者执行相应操作
},
cb:function(){
this.node[this.type] = this.value; // 订阅者执行相应操作
},
// 获取data的属性值
get: function() {
this.value = this.vm[this.name]; //触发相应属性的get
}
}

哎呦,咱们猜对了呢,这样双向数据绑定马上就要完成了,只剩一个Vue.nextTick()的地方了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/**
* 批处理构造函数
* @constructor
*/
function Batcher() {
this.reset();
}

/**
* 批处理重置
*/
Batcher.prototype.reset = function () {
this.has = {};
this.queue = [];
this.waiting = false;
};

/**
* 将事件添加到队列中
* @param job {Watcher} watcher事件
*/
Batcher.prototype.push = function (job) {
if (!this.has[job.name]) {
this.queue.push(job);
this.has[job.name] = job;
if (!this.waiting) {
this.waiting = true;
setTimeout(() => {
this.flush();
});
}
}
};

/**
* 执行并清空事件队列
*/
Batcher.prototype.flush = function () {
this.queue.forEach((job) => {
job.cb();
});
this.reset();
};

看完后是不是觉得超简单呢?

vue3版本将做出巨大的变化,把Dep跟Watcher都干掉了,html直接跟数据进行绑定,等vue3出来后,在写一篇关于vue的文章吧

DNS预读取的使用

Posted on 2018-12-18

X-DNS-Prefetch-Control 头控制着浏览器的 DNS 预读取功能。 DNS 预读取是一项使浏览器主动去执行域名解析的功能,其范围包括文档的所有链接,无论是图片的,CSS 的,还是 JavaScript 等其他用户能够点击的 URL。

因为预读取会在后台执行,所以 DNS 很可能在链接对应的东西出现之前就已经解析完毕。这能够减少用户点击链接时的延迟。

打开和关闭 DNS 预读取

你可以通过在服务器端发送 X-DNS-Prefetch-Control 报头,或是在文档中使用值为 http-equiv 的 <meta> 标签:

<meta http-equiv="x-dns-prefetch-control" content="on">

强制查询特定主机名

你可以通过使用 rel 属性值为 link type 中的 dns-prefetch 的 <link> 标签来对特定域名进行预读取:

<link rel="dns-prefetch" href="http://www.baidu.com/">

在这个例子中,Firefox将预解析域名”www.baidu.com"。

而且, 元素也可以使用不完整的 URL 的主机名来标记预解析,但这些主机名前必需要有双斜线:

<link rel="dns-prefetch" href="//www.baidu.com">

强制对域名进行预读取在有的情况下很有用, 比如, 在网站的主页上,强制在整个网站上频繁引用的域名的预解析,即使它们不在主页本身上使用。即使主页的性能可能不受影响,这将提高整体站点性能。

JavaScript 深入之 call 和 apply 的模拟实现

Posted on 2018-12-15

JavaScript 深入之 call 和 apply 的模拟实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 第一版
Function.prototype.call2 = function(context) {
// 首先要获取调用call的函数,用this可以获取
// this的指向为bar,因为bar是Funciton的实例
context.fn = this;
context.fn();
// 相当于把bar挂载到foo上,所以bar可以取到value
delete context.fn;
}

// 测试一下
var foo = {
value: 1
};

function bar() {
console.log(this.value);
}

bar.call2(foo); // 1

但是第一版不可以传递多个参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 第二版
Function.prototype.call2 = function(context) {
context.fn = this;
var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
eval('context.fn(' + args +')');
//这里 args 会自动调用 Array.toString() 这个方法。
//这里eval函数输出的是:context.fn(arguments[1],arguments[2])
delete context.fn;
}

// 测试一下
var foo = {
value: 1
};

function bar(name, age) {
console.log(name)
console.log(age)
console.log(this.value);
}

bar.call2(foo, 'kevin', 18);
// kevin
// 18
// 1

第二版的问题是,1.this 参数可以传 null,当为 null 的时候,视为指向 window2.函数是可以有返回值的!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// 第三版
Function.prototype.call2 = function (context) {
var context = context || window;
context.fn = this;

var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}

var result = eval('context.fn(' + args +')');
delete context.fn
return result;
}

// 测试一下
var value = 2;

var obj = {
value: 1
}

function bar(name, age) {
console.log(this.value);
return {
value: this.value,
name: name,
age: age
}
}

bar.call2(null); // 2

console.log(bar.call2(obj, 'kevin', 18));
// 1
// Object {
// value: 1,
// name: 'kevin',
// age: 18
// }

es6 版

1
2
3
4
5
6
7
Function.prototype.call2 = function (context, ...args) {
context = context || window
context.__fn__ = this
let result = context.__fn__(...args)
delete context.__fn__
return result
}

apply 的实现跟 call 类似,在这里直接给代码,代码来自于知乎 @郑航的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Function.prototype.apply = function (context, arr) {
var context = Object(context) || window;
context.fn = this;

var result;
if (!arr) {
result = context.fn();
}
else {
var args = [];
for (var i = 0, len = arr.length; i < len; i++) {
args.push('arr[' + i + ']');
}
result = eval('context.fn(' + args + ')')
}

delete context.fn
return result;
}

文章参考来源:https://github.com/mqyqingfeng/Blog/issues/11

12
陈晨

陈晨

前端,js,jquery,javascript,html5

16 posts
GitHub 简书 SF 掘金
© 2019 陈晨
Powered by Hexo
Theme - NexT.Pisces