在日常开发怎么选择每一个应用都是由各种组件组合而成

admin 发布于 2024-04-05 阅读(118)

本文会结合两种常见的编程范式,对js语言层面的一些特性进行复习,并对中的观点加以理解。

编程范式

前文我们讨论了编程范式,这里稍微回顾一下:

在我们编程中每解决一个问题对应,每种是多个的组合,而每种编程语言实现了一种或多种。

常见组合面向对象函数

OOP和FP是两种比较常见的编程范式,也被js同时支持,相关讨论有很多,这里只代表其中一种看法,更详细的会在后面章节介绍。

核心

按照前文说法,两种范式的区别就是FP比OOP少了一个state ,即

面向对象侧重于对数据的各种处理,数据是对象的内部状态, C++ Plus 对对象的本质表述为用来设计并扩展自己的数据结构:

The of - (OOP) is and your own data types

函数式侧重于数据流的流动,数据是外界的输入,并最终将对应结果返回,A Brief Intro to 对于函数式编程概括为一种数据流,而不是控制流

The core of is about data-flow than -flow

适用场景

以下结论参考 vs []

面向对象专家 也说过:

OO makes code by parts. FP makes code by parts.

面向对象的可读性是通过封装各部分,函数式的可读性是通过最小化各部分,即前者将数据和数据的动作进行封装,后者将各种操作拆分以最小化。

在日常开发怎么选择

每一个应用都是由各种组件组合而成,只不过组件的形式不固定,比如,data ,等。

不同编程语言倾向于使用不同的原子元素组成组件,比如java使用对象,使用等。而在js中,因为天然支持面向对象和函数式,因此在项目中往往混合使用,我们可以使用对象组合来为函数式编程生成数据类型,使用函数式编程为面向对象编程生成对象,不管怎么写,软件开发的本质就是组合。

The of is

我们的工作就是根据各种情况使用各种编程范式,像盖房子一样将各种组件组合起来,在具体讨论两种范式之前我们先多了解一下js本身,

js中的相关特性

我们需要先对js这种语言中的各种特性有所了解,才能利用范式这种工具更好的对我们开发的软件进行组合。这一部分主要会参考。

js语言的设计

Eich开发了js。他在这篇文章提到了js设计过程中的一些问题。

需要在浏览器中内置一种脚本语言,根据领导要求,首先需要像java(Look like Java,因此很多语法和java类似),而作者本人偏向于,因此最后在新的语言中选择了和一样的一等函数以及和Self一样的作为主要组成。受java影响,有了原始类型和对象的区分,比如和。

以上设计加上后来的发展就成了现在的js。

js特性概述

在js中的数据结构分为两种,原始类型和对象,js的对象创建并不是基于class的,而是有很多方式,比如字面量或者构造函数,每个构造函数都有一个属性用于实现基于原型(-based)的继承。一个构造函数的还有一个引用指向构造函数本身,当实现继承时,这个属性可能会改,按照惯例需要修正,但不是必须的(关于参考这里)。

说起构造函数,这里补充一点相关概念,函数是一种特殊的对象,含有 [[Call]],因此可以通过函数调用来执行相关代码,而构造函数又是一种特殊的函数,含有 [[]],可以通过new或super调用创建对象。

js中的对象原型链

每一个通过构造函数创建的对象都有一个指向构造函数属性的隐式引用(可以用访问但不推荐),而这个本身可能也有一个非null的引用指向它的,等等,这就被称为原型链。当访问对象的一个属性时,会首先从该对象本身查找,如果找不到就会沿着原型链依次查找,直到找到或者找到尽头发现没有,原型链上的属性可以被覆盖。

相较基于class继承的语言,通常来说,状态被实例拥有,方法被class拥有,继承的只有结构和行为(),而js这一切都是可以继承的。这里的。

原型链的尽头为null,要明确一个对象的原型链到底包括什么,这里可以大概分为以下

var obj={}
//原型链 obj=>Object.prototype=>null

var arr =[]
//arr=>Array.prototype=>Object.prototype=>null
//如果是自定义构造函数也一样
var P=function(){}
var p=new P()
//p=>P.prototype=>Object.prototype=>null

//1. 使用Object.create()
var q=Object.create(p)
// q=>p
//2. 直接修改构造函数的prototype
function Q(){}
Q.prototype=p
var q=new Q()
//q=>p
//3. 通过Object.setPrototypeOf(obj, prototype)设置__proto__属性,可以直接修改原型链,这个操作很浪费性能,少用
function P(){
    this.b=1
}
function Q(){
    this.a=2
}
var q=new Q()
Object.setPrototypeOf(q,P.prototype)
// q=>P.prototype
//4. 使用call和apply借用构造函数时,和原型无关
var P=function(v){
    this.a=v
}
function Q(v){
    P.call(this,v)
}
var q=new Q(2)
// q=>Q.prototype=>Object.prototype=>null

我们可以通过 判断一个构造函数的是否在指定对象的原型链中

function Q(){
    this.a=2
}
var q=new Q()
console.log(q instanceof Q)
console.log(q instanceof Object)

可以通过.()获得对象的属性

.上有一些属性和方法被其他所有对象继承,在特定对象继承过程中可能会对某些字段重写。

另外上还有很多静态方法用于处理关于对象的各种操作,具体请参考

js中的函数

在js中所有函数都是的实例,包括和本身,乃至各种内置构造函数(比如Array),因此有

Function.__proto__===Function.prototype//true
Function.prototype.__proto__===Object.prototype//true,即Function instanceof Object
//原型链 Function=>Function.prototype=>Object.prototype,以下类似
Object.__proto__===Function.prototype//true,即Object instanceof Function
Array.__proto__===Function.prototype //true
function a(){}
a.__proto__===Function.prototype//true

可见,所有函数的原型链到达.之前需要先经过.,一个函数是一个对象,更是一个函数。

.上有一些方法值得我们关注

其中前两个在一个对象的上下文应用另一个对象的方法,第三个用于修改上下文,其余参数会在返回的函数调用时使用

js的函数式编程

在具体的了解函数式编程之前,这里先了解一些概念,参考 。

概念Pure

一个纯函数是一个函数,符合以下特点

纯函数在函数式编程中很重要,但是实际的开发中,函数或多或少会有一些副作用,比如数据获取和操作dom。

函数复合是将两个或多个函数按照顺序生成一个函数或者执行操作。

State

共享数据可以是变量、对象或内存空间。使用共享数据的一个问题是为了了解一个函数的副作用,需要知道每个共享数据的操作历史,比如对一个用户信息在不同终端的修改会发生冲突,因此在flux中要使用单向流。

另一个问题是对共享数据的操作顺序也会造成不同结果,比如四则运算。

一个不可变对象是创建后就不能改变,但是js在语言层面只提供了原始类型的不可变性,对对象并不提供这种特性,即使使用.()等方法也只能冻结某个层级的对象修改,要想使用不可变数据,可以使用第三方库,比如Immer。

不可变对象是函数式编程的核心概念,没有不可变性,程序中的数据流就会不可控,应该使用原数据生成新数据,而不应该修改原来的数据。

在实际的操作中,对于一个特定的数据,不可变性和不同享,至少要满足一个。

Side

副作用指的是除了对输出结果操作以外其他的操作,比如打印日志或修改dom,副作用在函数式编程中应该避免,即将副作用和数据流处理分开。

Order

高阶函数是任何以函数作为参数或返回函数的函数,经常用于

, , Lists, and

这里包括上面提到的,可以参考, , And In

一个数据结构可以用于映射数据,比如 [1,2,3].map(x => x *2),换句话说,它是一个容器,会为内部的数据应用一个函数,当看到这个词时应该想到

在这里被映射的是一个数组,只要提供map api,其他数据结构应该也可以,一个按顺序处理的list可以看作是一个。

vs

函数式编程是一种声明式范式,声明式编程会将流控制过程抽象,而不是用一行行代码描述怎么做,对应的是命令式。比如函数命令式的写法

const doubleMap = numbers => {
const doubled = [];
for (let i = 0; i < numbers.length; i++) {
	doubled.push(numbers[i] * 2);
   }
 return doubled;
 };
 console.log(doubleMap([2, 3, 4])); // [4, 6, 8]

标签:  函数 编程 对象 原型 操作 

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。