Decorative image frame

我眼中的javascript函数式编程理解(一)

最简单的:

1
const restult = (x) => (y) => x + y;

那么他描述了一个什么事情呢?
result=x+y;

我们这里函数式编程就是很简单的理解为 结果=f(输入);

那么为啥要这样写呢?
我们举个例子奥:

1
2
const hi = (name) => `Hi ${name}`;
const greeting = (name) => hi(name);

这个代码满足了函数式编程的要求,然后我们发现我们其实是可以化简这个代码的!
这跟数学公式一样将式 1 带入式 2 那么就有

1
2
3
4
const hi = (name) => `Hi ${name}`;
const greeting = (name) => (name) => `Hi ${name}`;
//整理
const greeting = (name) => `Hi ${name}`;

最后就是一个式子:
const greeting= name=>Hi ${name};

如果你认为上面的代码不能体现出函数式编程的优势我们再来一个例子:
首先我们写一个非函数式编程的代码

1
2
3
4
//非函数式编程形式
const getServerStuff = (callback) => {
return ajaxCall((json) => callback(json));
};

我们将它化简,转化为函数式编程模式

1
const getServerStuff = (callback) => ajaxCall((json) => callback(json));

现在我们再看这个代码我们继续化简

1
2
3
const getServerStuff = (callback) => ajaxCall(callback);
//那么就会出现
const getServerStuff = ajaxCall;

这个代码事实上什么也没干跟上面,为什么这么说呢我们先按照函数式编程的思维思考:
他们为什么相等呢?
因为他们的输出入输出都相同!

双向绑定的思路

我先来个我的土法双向绑定

1
2
3
4
5
6
7
8
let obj = Object.create(null); //正好学完了原型和原型链我拿来玩玩
let odiv1 = document.getElementsByClassName("div1")[0];
let odiv2 = document.getElementsByClassName("div2")[0];
document.addEventListener("keyup", function (e) {
obj.hello = e.target.value;
odiv1.value = obj.hello;
odiv2.value = obj.hello;
});

在页面上测试成了!
但是!如果要是修改 obj.hello 还是不会变化的,因为它是伪双向绑定,数据的赋值需要靠 keyup 事件来出发。

但是这个均发生了变化!

1
2
3
4
5
6
7
8
9
10
11
12
13
let obj = {};
obj.hello = "";
let odiv1 = document.getElementsByClassName("div1")[0];
let odiv2 = document.getElementsByClassName("div2")[0];
Object.defineProperty(obj, "hello", {
set: function (v) {
odiv1.value = v;
odiv2.value = v;
},
});
document.addEventListener("keyup", function (e) {
obj.hello = e.target.value;
});

Javascript原型的深刻剖析

原文
对于使用过基于类的语言 (如 Java 或 C++) 的开发人员来说,JavaScript 有点令人困惑,因为它是动态的,并且本身不提供一个 class 实现。(在 ES2015/ES6 中引入了 class 关键字,但那只是语法糖,JavaScript 仍然是基于原型的)。
当谈到继承时,JavaScript 只有一种结构:对象。每个实例对象( object )都有一个私有属性(称之为 proto )指向它的构造函数的原型对象(prototype *)。该原型对象也有一个自己的原型对象( *proto ) ,层层向上直到一个对象的原型对象为 null。根据定义,null 没有原型,并作为这个原型链中的最后一个环节。
几乎所有 JavaScript 中的对象都是位于原型链顶端的 Object 的实例。
尽管这种原型继承通常被认为是 JavaScript 的弱点之一,但是原型继承模型本身实际上比经典模型更强大。例如,在原型模型的基础上构建经典模型相当简单。

不懂没关系,看图
image.png

那么 Javascript 的逻辑结构其实就清晰了:我拿 window 举个栗子
null->{constructor….}->EventTaget->WindowProperties->Window->window

而真正的数据结构是反过来的:
我们把箭头方向倒过来就好了,原理也是很简单.

1
2
3
4
5
function doSomething() {}
doSomething.prototype.foo = "bar"; // add a property onto the prototype
var doSomeInstancing = new doSomething();
doSomeInstancing.prop = "some value"; // add a property onto the object
console.log(doSomeInstancing);

返回结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
prop: "some value",
__proto__: {
foo: "bar",
constructor: ƒ doSomething(),
__proto__: {
constructor: ƒ Object(),
hasOwnProperty: ƒ hasOwnProperty(),
isPrototypeOf: ƒ isPrototypeOf(),
propertyIsEnumerable: ƒ propertyIsEnumerable(),
toLocaleString: ƒ toLocaleString(),
toString: ƒ toString(),
valueOf: ƒ valueOf()
}
}
}

###

总结:4 个用于拓展原型链的方法

下面列举四种用于拓展原型链的方法,以及他们的优势和缺陷。下列四个例子都创建了完全相同的 inst 对象(所以在控制台上的输出也是一致的),为了举例,唯一的区别是他们的创建方法不同。

名称 例子 优势 缺陷
New-initialization ```

function foo(){}
foo.prototype = {
foo_prop: “foo val”
};
function bar(){}
var proto = new foo;
proto.bar_prop = “bar val”;
bar.prototype = proto;
var inst = new bar;
console.log(inst.foo_prop);
console.log(inst.bar_prop);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 | 支持目前以及所有可想象到的浏览器(IE5.5都可以使用). 这种方法非常快,非常符合标准,并且充分利用JIST优化。 | 为使用此方法,这个问题中的函数必须要被初始化。 在这个初始化过程中,构造可以存储一个唯一的信息,并强制在每个对象中生成。但是,这个一次性生成的独特信息,可能会带来潜在的问题。另外,构造函数的初始化,可能会给生成对象带来并不想要的方法。 然而,如果你只在自己的代码中使用,你也清楚(或有通过注释等写明)各段代码在做什么,这些在大体上都根本不是问题(事实上,还常常是有益处的)。 |
| Object.create | ```
function foo(){}
foo.prototype = {
foo_prop: "foo val"
};
function bar(){}
var proto = Object.create(
foo.prototype
);
proto.bar_prop = "bar val";
bar.prototype = proto;
var inst = new bar;
console.log(inst.foo_prop);
console.log(inst.bar_prop);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function foo(){}
foo.prototype = {
foo_prop: "foo val"
};
function bar(){}
var proto = Object.create(
foo.prototype,
{
bar_prop: {
value: "bar val"
}
}
);
bar.prototype = proto;
var inst = new bar;
console.log(inst.foo_prop);
console.log(inst.bar_prop)

| 支持当前所有非微软版本或者 IE9 以上版本的浏览器。允许一次性地直接设置 __proto__ 属性,以便浏览器能更好地优化对象。同时允许通过 Object.create(null)来创建一个没有原型的对象。 | 不支持 IE8 以下的版本。然而,随着微软不再对系统中运行的旧版本浏览器提供支持,这将不是在大多数应用中的主要问题。 另外,这个慢对象初始化在使用第二个参数的时候有可能成为一个性能黑洞,因为每个对象的描述符属性都有自己的描述对象。当以对象的格式处理成百上千的对象描述的时候,可能会造成严重的性能问题。 |
| Object.setPrototypeOf | ```
function foo(){}
foo.prototype = {
foo_prop: “foo val”
};
function bar(){}
var proto = {
bar_prop: “bar val”
};
Object.setPrototypeOf(
proto, foo.prototype
);
bar.prototype = proto;
var inst = new bar;
console.log(inst.foo_prop);
console.log(inst.bar_prop);

1
2


function foo(){}
foo.prototype = {
foo_prop: “foo val”
};
function bar(){}
var proto;
proto=Object.setPrototypeOf(
{ bar_prop: “bar val” },
foo.prototype
);
bar.prototype = proto;
var inst = new bar;
console.log(inst.foo_prop);
console.log(inst.bar_prop)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 | 支持所有现代浏览器和微软IE9+浏览器。允许动态操作对象的原型,甚至能强制给通过 `Object.create(null) `创建出来的没有原型的对象添加一个原型。 | 这个方式表现并不好,应该被弃用。如果你在生产环境中使用这个方法,那么快速运行 Javascript 就是不可能的,因为许多浏览器优化了原型,尝试在调用实例之前猜测方法在内存中的位置,但是动态设置原型干扰了所有的优化,甚至可能使浏览器为了运行成功,使用完全未经优化的代码进行重编译。 不支持 IE8 及以下的浏览器版本。 |
| __proto__ | ```
function foo(){}
foo.prototype = {
foo_prop: "foo val"
};
function bar(){}
var proto = {
bar_prop: "bar val",
__proto__: foo.prototype
};
bar.prototype = proto;
var inst = new bar;
console.log(inst.foo_prop);
console.log(inst.bar_prop);
1
2
3
4
5
6
7
8
9
10
11
var inst = {
__proto__: {
bar_prop: "bar val",
__proto__: {
foo_prop: "foo val",
__proto__: Object.prototype
}
}
};
console.log(inst.foo_prop);
console.log(inst.bar_prop)

| 支持所有现代非微软版本以及 IE11 以上版本的浏览器。将 __proto__ 设置为非对象的值会静默失败,并不会抛出错误。 | 应该完全将其抛弃因为这个行为完全不具备性能可言。 如果你在生产环境中使用这个方法,那么快速运行 Javascript 就是不可能的,因为许多浏览器优化了原型,尝试在调用实例之前猜测方法在内存中的位置,但是动态设置原型干扰了所有的优化,甚至可能使浏览器为了运行成功,使用完全未经优化的代码进行重编译。不支持 IE10 及以下的浏览器版本。 |

vue-router的原理和最简实现

vue javascript vue-router

Vue 的机制和 react 有很大差别,他的 router 的设计也是足够奇思妙想,而且符合编程逻辑

先从配置说起,要在 webpack 环境下读取 vue 文件必须安装 vue-loader 插件image.png
这样配置即可。
之后开始搞事情~

模仿官方正版的 vue-router 的文件结构,我配置了一个 vue-router 类似的 routers.js

1
2
3
4
export default {
"/": "Home",
"/about": "About",
};

然后在 main.js 里进行注入:
下面看我的注释来讲解这些代码的具体含义

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
import Vue from "vue";
import routes from "./routes";

const app = new Vue({
el: "#app",
data: {
//初始化当前路由位置
currentRoute: window.location.pathname,
},
computed: {
//在vue的computed函数中进行逻辑计算
ViewComponent() {
// 在刚才定义的routers文件里面找是否有带当前路径的router注册,有就会返回routes[this.currentRoute]的值,没有就会返回undefined
const matchingView = routes[this.currentRoute];
//这里跟vue原版的router不一样的是它在这里请求组件的,那我们也可以改成和原版一样的模式等会看下面
return matchingView
? require("./pages/" + matchingView + ".vue")
: require("./pages/404.vue");
},
},
render(h) {
//那么现在返回的就是最关键的组件,这里面只有一个vue实例,这就是传说中的vue单页应用
//但是我们也可以开多个实例。
return h(this.ViewComponent);
},
});
//这里我们看到了一个非常有意思的写法,这里是用来监控路径发生变化的,具体看下面的链接
window.addEventListener("popstate", () => {
app.currentRoute = window.location.pathname;
});

对 popstate 事件的介绍和使用方法

下面,我们将在官方给的示例的基础上继续推进,争取还原。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import Home from "./pages/Home.vue";
import About from "./pages/About.vue";

let routes = [
{
path: "/",
name: "home",
component: Home,
},
{
path: "/about",
name: "about",
component: About,
},
];
export default routers;
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
import Vue from "vue";
import routes from "./routes";

const app = new Vue({
el: "#app",
data: {
currentRoute: window.location.pathname,
},
computed: {
ViewComponent() {
for (var i = 0; i < routes.length; i++) {
if (routes[i].path == this.currentRoute) {
return routes[i].component;
} else {
return Erro;
}
}
},
},
render(h) {
return h(this.ViewComponent);
},
});

window.addEventListener("popstate", () => {
app.currentRoute = window.location.pathname;
});

这个完成的其实也不是很漂亮,正常应该在 vue 里面去接受 router,然后再在单独的文件去修改,做到解耦。

最后:
参照思路

实现Get请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function getJSON(url) {
return new Promise(function (resolve, reject) {
var XHR = new XMLHttpRequest();
XHR.open("GET", url, true);
XHR.send();
XHR.onreadystatechange = function () {
if (XHR.readyState == 4) {
if (XHR.status == 200) {
try {
var response = JSON.parse(XHR.responseText);
resolve(response);
} catch (e) {
reject(e);
}
} else {
reject(new Error(XHR.statusText));
}
}
};
});
}
//用法
getJSON(url).then((resp) => console.log(resp));