八大最常用的JavaScript设计模式

article/2025/10/25 15:59:43

八大最常用的JavaScript设计模式

设计模式(Design pattern) 是解决软件开发某些特定问题而提出的一些解决方案也可以理解成解决问题的一些思路。通过设计模式可以帮助我们增强代码的可重用性、可扩充性、 可维护性、灵活性好。我们使用设计模式最终的目的是实现代码的 高内聚低耦合。通俗一点讲的话 打比方面试官经常会问你如何让代码有健壮性。其实把代码中的变与不变分离,确保变化的部分灵活、不变的部分稳定,这样的封装变化就是代码健壮性的关键。而设计模式的出现,就是帮我们写出这样的代码。 设计模式就是解决编程里某类问题的通用模板,总结出来的代码套路就是设计模式。本文章总结下JS在工作中常用的设计模式 ,以帮助大家提高代码性能,增强工作效率!

JavaScript设计模式(一)装饰器模式

img

圣诞节要到了,许多家庭会买一颗松树装上彩灯,一闪一闪亮晶晶然后摇身一变成了圣诞树。这里 的彩灯就是装饰器,他不会对松树原有的功能产生影响。(还是本来的树)

这种给对象动态地增加职责的方式称为装 饰器(decorator)模式。装饰器模式能够在不改 变对象自身的基础上,在程序运行期间给对象 动态地添加职责。

应用

当我们接手老代码,需要对它已有的功能做个拓展。

``

var horribleCode = function(){console.log(’我是一堆你看不懂的老逻辑')}// 改成:var horribleCode = function(){console.log('我是一堆你看不懂的老逻辑')console.log('我是新的逻辑')
}

这样做有很多的问题。直接去修改已有的函数体,违背了我们的“开放封闭原则”;往一个函数体里塞这么多逻辑,违背了我们的“单一职责原则”。

为了不被已有的业务逻辑干扰,将旧逻辑与新逻辑分离,把旧逻辑抽出去:

var horribleCode = function(){console.log(’我是一堆你看不懂的老逻辑')}var _horribleCode = horribleCodehorribleCode = function() {_horribleCode()console.log('我是新的逻辑')}horribleCode()

这样就完成了旧代码的运用 以及新代码的无伤添加了!!!

JavaScript设计模式(二)工厂模式

img

先来理解一个概念 —— 构造器模式

你开了家动物园,只有两只动物,你可能会这样录入系统:

const monkey = {name: '悟空'age: '1'
}const tiger = {name: '泰格伍兹'age: '3'
}

如果你的动物越来越多,对象字面量也会越来越多,这个时候构造函数可以自动创建动物对象

    this.name = namethis.age = age
}const animal = new Animal(name, age) //Animal 就是一个构造器

像 Animal 这样当新建对象的内存被分配后,用来初始化该对象的特殊函数,就叫做构造器。在 JavaScript 中,我们使用构造函数去初始化对象,就是应用了构造器模式

可以看出每个实例化后 对象( animal )属性的key (name,age) 是不变的,对应的value(空空,泰格伍兹)是变的。所以构造器将赋值过程封装,确保了每个对象属性固定,开放了取值确保个性灵活。

简单工厂模式

动物园要求根据每个动物的食性喜好来分配不同的食物。这样之前封装的Animal 就不能直接用了,我们重新封装的构造器。

 this.name = namethis.age = agethis.favorite = 'fruit'this.food = [apple, banaba]
}function Carnivore (name,age) {this.name = namethis.age = agethis.favorite = 'meat'this.food = [beef, pork]
}

根据喜好可以分配相应的

function Factory(name, age, favorite) {switch(career) {case 'fruit':return new Vegetarian(name, age) breakcase 'meat':return new Carnivore(name, age)break...
}

总结

工厂模式:将创建对象的过程单独封装。

应用场景:有构造函数的地方、写了大量构造函数、调用了大量的 new的情况下

JavaScript设计模式(三)单例模式

img

单例模式

保证仅有一个实例,并提供一个访问它的全局访问点,这样的模式就叫做单例模式。然后性能得到优化!

以下代码我们做一个弹窗 如果实例已经创建过 就无需再次创建 这就是单例!

  <body><input type="button" id="btn1" value="成功"><input type="button" id="btn2" value="失败"><input type="button" id="btn3" value="警告">
</body>
<script>const obj = {init:function(){this.ele = document.createElement("dialog"),document.body.appendChild(this.ele);},show:function(c, t){// 每次显示之前先判断是否已经存在弹出框元素,如果不存在就创建,如果已经存在就不用重新创建,只需要修改样式和显示即可if(!this.ele){this.init();}this.ele.style.borderColor = c;this.ele.style.color = c;this.ele.innerHTML = t;this.ele.style.display = "block";clearTimeout(this.t);this.t = setTimeout(()=>{this.hide();}, 2000);},hide:function(){this.ele.style.display = "none";}}const obtn1 = document.getElementById("btn1")const obtn2 = document.getElementById("btn2")const obtn3 = document.getElementById("btn3")obtn1.onclick = function(){obj.show("green", "成功");}obtn2.onclick = function(){obj.show("red", "失败");}obtn3.onclick = function(){obj.show("yellow", "警告");}
</script>

总结

优点:适用于单一对象,只生成一个对象实例,避免频繁创建和销毁实例,减少内存占用。

缺点:不适用动态扩展对象,或需创建多个相似对象的场景。

JavaScript设计模式(四)-适配器模式

img

当电脑需要外接显示器的时候,我们都会用到下面这个东西。转换器帮助我们在不用更改笔记本接口的同时可以适配HDMI。

将转换器抽象到代码层面就是今天要介绍的适配器了。

适配器模式的作用是解决两个软件实体间的接口不兼容的问题。使用适配器模式之后,原本 由于接口不兼容而不能工作的两个软件实体可以一起工作。

适配器模式(结构型)

应用举例: 点外卖的时候有美团,饿了么可以选择,同一家店如果要对比两个平台的价格来回切换App十分不方便,作为一个Coder能用代码解决的坚决不用人力。这个时候我们就想到写个小应用对比两家的价格。

在他们openapi里找到了对应的方法,发现请求不一样,入参不一样,返回的数据结构也不一样。翻译成伪代码就是如下的状态

class Eleme() {getElePice() {console.log('在饿了么上商品的价格')return {elePrice:xx}}
} class Meituan() {getMeiPice() {console.log('在美团上商品的价格')return {meiPrice:xx}}
}

试想一下,如果再多增加一些其他平台,前端渲染的时候要写多少个if else去判断来源。这个时候我们可以通过引入适配器

class ElemeAdapter() {getPrice () {const e =  new Eleme()return { price:e.elePrice}}}class MeituanAdapter() {getPrice () {const m =  new Meituan()return { price:m.meiPrice}}}//通过适配器拿到的数据格式都是统一的 {price:xx}//同样,入参也可以在适配器中统一处理

虽然这种模式很简单,但还有很多场景运用到了适配器模式。如axios抹平了web和node环境下api的调用差异、React的高阶组件等。适配器不会去改变实现层,那不属于它的职责范围,它干涉了抽象的过程。外部接口的适配能够让同一个方法适用于多种系统。

总结

适配器模式主要用来解决两个已有接口之间不匹配的问题,它不考虑这些接口是怎样实 现的,也不考虑它们将来可能会如何演化。适配器模式不需要改变已有的接口,就能够 使它们协同作用。

JavaScript设计模式(四)-适配器模式

img

代理,顾名思义就是帮助别人做事,GoF对代理模式的定义如下:

代理模式(Proxy),为其他对象提供一种代理以控制对这个对象的访问。

代理模式使得代理对象控制具体对象的引用。代理几乎可以是任何对象:文件,资源,内存中的对象,或者是一些难以复制的东西。

// 我们来举一个简单的例子,假如dudu要送酸奶小妹玫瑰花,却不知道她的联系方式或者不好意思,想委托大叔去送这些玫瑰,
// 那大叔就是个代理(其实挺好的,可以扣几朵给媳妇),那我们如何来做呢?// 先声明美女对象
var girl = function (name) {this.name = name;
};
// 这是dudu
var dudu = function (girl) {this.girl = girl;this.sendGift = function (gift) {alert("Hi " + girl.name + ", dudu送你一个礼物:" + gift);}
};
// 大叔是代理
var proxyTom = function (girl) {this.girl = girl;this.sendGift = function (gift) {(new dudu(girl)).sendGift(gift); // 替dudu送花咯}
};

调用

var proxy = new proxyTom(new girl("酸奶小妹"));
proxy.sendGift("999朵玫瑰");

远程代理,也就是为了一个对象在不同的地址空间提供局部代表,这样可以隐藏一个对象存在于不同地址空间的事实,就像web service里的代理类一样。

虚拟代理,根据需要创建开销很大的对象,通过它来存放实例化需要很长时间的真实对象,比如浏览器的渲染的时候先显示问题,

而图片可以慢慢显示(就是通过虚拟代理代替了真实的图片,此时虚 拟代理保存了真实图片的路径和尺寸。

安全代理,用来控制真实对象访问时的权限,一般用于对象应该有不同的访问权限。

JavaScript设计模式(五)-发布订阅模式

img

今年非常火爆的苹果13, 非常火爆。我每天都会去亚马逊上看看货到没,可他一直处在无货状态,如果他十年不上线,难道我要十年如一日的去看吗。好在亚马逊提供了一个 到货通知 的按钮,订阅到货通知后,只要健身环一到,就会发信息告诉我。

上述就是一个现实中的发布-订阅者模式。我和其他同样想买健身环的买家都属于 订阅者,我们订阅了到货消息,亚马逊作为发布者,当货物到达时会给我们发布货物到货信息。

发布—订阅模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个目标对象,当这个目标对象的状态发生变化时,会通知所有观察者对象,使它们能够自动更新。

发布—订阅模式 (行为型)

实现了一个最简单的发布—订阅模式

例子

//发布者 亚马逊class Publisher() {construct() {this.observers = [] }//添加订阅者add(oberver) {this.observers.push(observer)}// 通知所有订阅者notify() {this.observers.forEach((observer) => {//调用订阅者的函数observer.update(this) })}
}
// 订阅者类 顾客
class Observer {constructor() {}update() {console.log('Observer  buy buy buy')}
}
const cunstomer = new CunstomerObserver() //创建订阅者:顾客
const amazon = new Publisher() / /亚马逊amazon.add(cunstomer) //订阅到货小修消息amazon.notify() //货物到达通知顾客

JavaScript设计模式(六)-策略模式

img

策略模式定义一族算法类,将每个算法分别封装起来,让它们可以互相替换。策略模式可以使算法的变化独立于使用它们的客户端(这里的客户端代指使用算法的代码)。策略模式用来解耦策略的定义、创建、使用。实际上,一个完整的策略模式就是由这三个部分组成的。

策略类的定义比较简单,包含一个策略接口和一组实现这个接口的策略类。策略的创建由工厂类来完成,封装策略创建的细节。策略模式包含一组策略可选,客户端代码选择使用哪个策略,有两种确定方法:编译时静态确定和运行时动态确定。其中,“运行时动态确定”才是策略模式最典型的应用场景。

大家看这段代码可以看见这里有一堆的if,随着组件的增多if变得庞大难以维护。通过今天的策略模式我们的代码可以大瘦身!

调用

function initNewDataList (avaliable = []) {avaliable.forEach( (item, index) => {if (item.type === 'singleBanner') {//加载组件}if (item.type === 'groupBanner') {//加载组件}if (item.type === 'tab') {//加载组件}})}

改造

function singleBannerFunc (item,index) {//加载组件
} 
function groupBannerFunc (item,index) {// 加载组件
}function tabFunc (item,index) {// 加载组件
}function initNewDataList (avaliable = []) {avaliable.forEach( (item, index) =>{if (item.type === 'singleBanner') {singleBannerFunc()}if (item.type === 'groupBanner') {groupBannerFunc()}if (item.type === 'tab') {tabFunc()}})
}

总结

策略模式利用组合、委托和多态等技术和思想,可以有效地避免多重条件选择语句。

优点

  • 策略模式提供了对开放—封闭原则的完美支持,将算法封装在独立的 strategy 中,使得它 们易于切换,易于理解,易于扩展。
  • 策略模式中的算法也可以复用在系统的其他地方,从而避免许多重复的复制粘贴工作。
  • 在策略模式中利用组合和委托来让 Context 拥有执行算法的能力,这也是继承的一种更轻 便的替代方案。

缺点:

使用策略模式会在程序中增加许多策略类或者策略对象,但实际上这比把它们负责的 逻辑堆砌在 Context 中要好。

JavaScript设计模式(六)-外观模式整合调用

img

  1. 定义

为子系统中的一组接口提供一个一致的界面,定义一个高层接口,这个接口使子系统更加容易使用

  1. 核心

可以通过请求外观接口来达到访问子系统,也可以选择越过外观来直接访问子系统

  1. 实现

外观模式在JS中,可以认为是一组函数的集合

调用

// 三个处理函数
function start() {console.log('start');
}function doing() {console.log('doing');
}function end() {console.log('end');
}// 外观函数,将一些处理统一起来,方便调用
function execute() {start();doing();end();
}// 调用init开始执行
function init() {// 此处直接调用了高层函数,也可以选择越过它直接调用相关的函数execute();
}init(); // start doing end

JavaScript设计模式(七)-多态模式

img

多态

熟悉java的朋友知道,java三大特征之一就有多态,多态给java带来了很大的灵活性,很多设计模式也是通过多态来实现,java中的多态涉及到向上转型和向下转型,而javascript(以下简称js)的"多态"就相对来说容易实现

我们来看一段“多态”的js代码

多态就是可以让函数一个函数根据不同的传参有不同的返回值或者不同的执行过程,让函数更加灵活!!!

JS中可以根据argumengts的特性进行 同一函数返回不同的值或者不同的执行过程实现多态模式!

示例代码

<script>function Person() {this.test1 = function () {if (arguments.length == 1) {this.show1(arguments[0]);} else if (arguments.length == 2) {this.show2(arguments[0], arguments[1]);} else if (arguments.length == 3) {this.show3(arguments[0], arguments[1], arguments[2]);}};this.show1 = function (a) {window.alert("show1()被调用" + a);};this.show2 = function (a, b) {window.alert("show2()被调用" + "--" + a + "--" + b);};function show3(a, b, c) {window.alert("show3()被调用");}}var p1 = new Person();p1.test1("a", "b");p1.test1("a");</script>

JavaScript设计模式(八)-迭代器模式

img

迭代器模式也叫游标模式,它用来遍历集合对象。这里说的“集合对象”,我们也可以叫“容器”“聚合对象”,实际上就是包含一组对象的对象,比如,数组、链表、树、图、跳表。迭代器模式主要作用是解耦容器代码和遍历代码。大部分编程语言都提供了现成的迭代器可以使用,我们不需要从零开始开发。

迭代器模式**:指提供一种方法顺序访问一个聚合对象中的哥哥元素,而又不需要暴露该对象的内部表示。

// jQuery 中的迭代器模式
$.each([1, 2, 3], function(i, n) {console.log('当前下标为:' + i)console.log('当前的值为:' + n)
})

外部迭代器

const Iterator = function(obj) {let current = 0;const next = function() {current += 1}const isDone = function() {return current >= obj.length}const getCurrentItem = function() {return obj[ current ]}return {next,isDone,getCurrentItem,length: obj.length}
}// 比较两个数组的元素是否相等
const compare = function(iterator1, iterator2) {if (iterator1.length !== iterator2.length) return falsewhilte(!iterator1.isDone() && !iterator2.isDone()) {if (iterator1.getCurrentItem() !== iterator2.getCurrentItem()) return false// 迭代iterator1.next()iterator2.next()  }// 相等return true
}

迭代器模式是一种相对简单的模式,简单到很多时候我们都不认为它是一种设计模式。大部分语言都内置有迭代器模式。

总结

设计模式是为了可复用、可拓展、高性能软件,前人给我们总结的宝贵经验。

设计模式(Design Pattern)是前辈们对代码开发经验的总结,是解决特定问题的一系列套路。它不是语法规定,而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案。

当然,软件设计模式只是一个引导,在实际的软件开发中,必须根据具体的需求来选择

总结

组的元素是否相等
const compare = function(iterator1, iterator2) {
if (iterator1.length !== iterator2.length) return false

whilte(!iterator1.isDone() && !iterator2.isDone()) {
if (iterator1.getCurrentItem() !== iterator2.getCurrentItem()) return false

// 迭代
iterator1.next()
iterator2.next()  

}

// 相等
return true
}


迭代器模式是一种相对简单的模式,简单到很多时候我们都不认为它是一种设计模式。大部分语言都内置有迭代器模式。**总结**设计模式是为了可复用、可拓展、高性能软件,前人给我们总结的宝贵经验。设计模式(Design Pattern)是前辈们对代码开发经验的总结,是解决特定问题的一系列套路。它不是语法规定,而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案。当然,软件设计模式只是一个引导,在实际的软件开发中,必须根据具体的需求来选择## **总结**发布—订阅模式的优点: 时间上的解耦,对象之间的解耦

http://chatgpt.dhexx.cn/article/cn8YuCbX.shtml

相关文章

JavaScript 设计模式之组合模式

引 我们知道地球和一些其他行星围绕着太阳旋转&#xff0c;也知道在一个原子中&#xff0c;有许多电子围绕着原子核旋转。我曾经想象&#xff0c;我们的太阳系也许是一个更大世界里的一个原子&#xff0c;地球只是围绕着太阳原子的一个电子。而我身上的每个原子又是一个星系&a…

JavaScript设计模式

JavaScript设计模式 设计模式&#xff1a;代码经验的总结&#xff0c;是可重用的用于解决软件设计中一般问题的方案。 设计模式都是面向对象的。 学习设计模式&#xff0c;有助于写出可复用和可维护性高的程序。 常用的12种设计模式&#xff1a; 工厂模式 单例模式 原型模…

JavaScript中常见的十五种设计模式

一、单例模式 二、策略模式 三、代理模式 四、迭代器模式 五、发布—订阅模式 六、命令模式 七、组合模式 八、模板方法模式 九、享元模式 十、职责链模式 十一、中介者模式 十二、装饰者模式 十三、状态模式 十四、适配器模式 十五、外观模式 一、单例模式 1. …

JS 常用的六种设计模式介绍

常用设计模式 前言 我们经常听到一句话&#xff0c;“写代码要有良好的封装&#xff0c;要高内聚&#xff0c;低耦合”。究竟怎样的代码才算得上是良好的代码。 什么是高内聚&#xff0c;低耦合&#xff1f; 即五大基本原则&#xff08;SOLID&#xff09;的简写 高层模块不…

git工具统计项目的代码行数

1、git 查看代码的项目总行数 &#xff08;1&#xff09;打开Git终端&#xff0c;进入项目的根目录 git log --prettytformat: --numstat | awk { add $1; subs $2; loc $1 - $2 } END { printf "added lines: %s, removed lines: %s, total lines: %s\n", add,…

小技巧之统计代码行数

欢迎关注我的微信公众号“人小路远”哦&#xff0c;在这里我将会记录自己日常学习的点滴收获与大家分享&#xff0c;以后也可能会定期记录一下自己在外读博的所见所闻&#xff0c;希望大家喜欢&#xff0c;感谢支持&#xff01; 搞了两个月&#xff0c;连搬带抄写出来的代码&a…

MAC代码下统计代码行数工具

作为一名程序员在很多的时候需要统计代码行数&#xff1a; 支持windows系统的代码行数统计方法以及软件很多&#xff0c;但是MAC系统的统计代码行数的真的不太多。 大家都知道用 wc -l 命令进行代码行数统计&#xff0c;但是它会将代码中的注释、空行所占用的文本行都统计在内…

cloc工具 命令行 统计代码行数

基本用法 &#xff1a;cloc后面跟目录名&#xff0c;文件名&#xff0c;或压缩文件名 例如&#xff1a; cloc ./application 1.安装(参考官网http://cloc.sourceforge.net/#apt-get) 根据操作系统不同&#xff0c;选择以下任意安装方法 sudo npm install -g cloc …

程序代码行数统计

程序写完了&#xff0c;提交著作权的时候不知道代码行数是多少怎么办&#xff1f; 介绍 软件名称兼容系统下载地址代码统计工具Windows软件下载 下面我们开始教程 打开主应用程序点击加号添加程序项目所在的目录点击按钮选择需要统计文件的文件后缀&#xff0c;看个人需求如…

计代码行数cloc,一个代码统计行数很好用的工具

分为window、mac系统区分&#xff0c;基本是一样的&#xff0c;一个代码统计行数很好用的工具。 CLOC简介 Cloc是一款使用Perl语言开发的开源代码统计工具&#xff0c;支持多平台使用、多语言识别&#xff0c;能够计算指定目标文件或文件夹中的文件数&#xff08;files&#x…

统计代码量-代码统计工具 CLOC | gitlab统计代码量

文章目录 一、代码统计工具 CLOC什么是CLOC?下载安装clocs使用 二、gitlab统计代码量命令行统计图形化统计IDE Statistic统计代码插件 一、代码统计工具 CLOC 什么是CLOC? github: https://github.com/AlDanial/cloc CLOC是Count Lines of Code的意思&#xff0c;可以计算…

Python实现一个代码行数统计工具(以C/C++为例)

前几天在网上看到一个有意思的题&#xff0c;题目是设计一个代码行数统计工具。这类工具我经常会用到&#xff0c;但是具体是如何实现的呢&#xff1f;这个问题我还从未思考过&#xff0c;于是便试着做出这种工具。 题目描述是这样的&#xff1a; 题目要求&#xff1a;   请…

统计项目代码行数工具cloc

Ubuntu用户 使用cloc在ubuntu内统计代码行数 安装cloc工具 sudo apt-get install cloc进入需要统计的目录内&#xff0c;然后执行 cloc .然后就会显示文件目录中的文件数(files)、空白行数(blank)、注释行数(comment)和代码行数(code)。 Windows 用户 也是使用cloc工具 …

Win10 代码行数统计工具CLOC的安装和使用

简介 CLOC(Count Lines of Code)&#xff0c;是一个可以统计多种编程语言中空行、评论行和物理行的工具。这个工具还是蛮实用的&#xff0c;可以帮我们快速了解一个项目中代码的信息。 注&#xff1a;底下这个命令可以实现统计代码行数的功能&#xff0c;只是不排除空行和注释…

代码行数统计小工具

一、先下载好SourceCounter小工具。解压&#xff0c;然后直接打开文件夹中的SourceCounter.exe。如果没有找到此工具的下载链接&#xff0c;点这里下载 二、选择代码类型&#xff0c;勾选上所有类型 三、双击点开后&#xff0c;选择文件夹&#xff0c;就可以直接统计出字…

在项目开发中统计代码行数的6种方式

文章目录 一、使用find和wc命令统计代码行数进行参数的过滤筛选命令参数简要说明 二、PowerShell工具统计代码行数条件过滤输出所有文件的行数PowerShell相关命令的简要说明 三、git命令git ls-filesgit log 四、代码编辑器插件五、jscpd六、自己实现一个注释和空行忽略目录和文…

chatgpt赋能python:Python代码行数统计-统计Python代码行数的常用工具与使用方法

Python代码行数统计 - 统计Python代码行数的常用工具与使用方法 Python编程语言是当今最流行的编程语言之一&#xff0c;在数据科学、人工智能、Web应用程序等许多领域都得到了广泛应用。当我们开发Python项目时&#xff0c;我们经常需要统计代码行数以管理代码库并监视进度。…

局域网电脑使用同一台鼠标键盘控制

问题又来了,我现在有两个电脑&#xff0c;局域网相连&#xff0c;但是我只有一套键盘鼠标啊&#xff0c;办公特别不方便&#xff0c;现在有工具可以让我们达到这个目的。 微软推出的 Mouse without Borders (无界鼠标)&#xff0c;这是一个免费的工具大家可以放心。下载地址为…

用一套键鼠控制两台主机

自己最近做一个项目涉及到以下场景&#xff1a;需要在一台window10系统的电脑上看一些文档&#xff0c;同时在一台装Ubuntu1804的电脑上跑一些代码。因此需要同时用两套鼠标键盘&#xff0c;切换起来十分麻烦&#xff0c;而且经常会拿错。因此参考博客解决了用一套键鼠控制两台…

Synergy两台电脑使用同一个鼠标和键盘

###分享一款共享鼠标和键盘的软件&#xff0c;即两台电脑使用同一个鼠标和键盘&#xff01; Synergy是一款跨平台的键盘鼠标共享软件&#xff0c;日前我们提供了Synergy 和Synergy 64位的Win版本、Synergy Mac版&#xff0c;Synergy 能够让使用者仅用一套键盘鼠标&#xff0c;就…