JavaScript 设计模式

发表于 1年以前  | 总阅读数:372 次

大家好,在这篇文章中我将介绍设计模式是什么以及为什么很重要。

我还将介绍一些最流行的设计模式,并为每一种模式举例说明。让我们开始吧!

目录

  • 什么是设计模式

  • 创建范例

  • 单例模式

  • 工厂方法

  • 抽象工厂

  • 构造器

  • 原型

  • 结构范例

  • 适配器

  • 装饰

  • 外观

  • 代理

  • 行为范式

  • 责任链

  • 迭代器

  • 观察者

  • 总结

什么是设计模式

设计模式这个概念是由一本名为《设计模式:可复用面向对象软件的基础》的书推广而来,这本书在1994年由四个C++工程师编写的。

这本书探讨了面向对象的编程的能力和陷阱,并介绍了23种可以用来解决编程问题的模式。

这些模式并不是算法或者具体的实现。它们更像是想法、观点和抽象,辅助你去解决一些特定问题。

根据要素的不同模式的实现也各不相同,重要的是模式背后的概念,它可以帮助我们更好地解决问题。

话虽如此,但是请记住,这些模式建立在C++的OOP的基础之上,当使用更现代的编程语言如JavaScript时,模式可能不等效,甚至给代码添加了不必要的样本。

不过把这些模式当作一般的编程知识来了解没有坏处。

旁注:如果你不熟悉编程范式或者OOP,推荐你阅读我最近写的这两篇文章。

设计模式的简介就到这里。设计模式可以被分为三大类:创建、结构、行为范例。让我们逐个了解。

创建范例

创建范例包括不同的创建对象的机制。

单例模式

单例模式确保对象的类只有一个不可更改实例。简言之,单例模式包含一个不能被复制和修改的对象。当你希望应用遵循“真理的单点性”的观点时,这个模式就能发挥作用。

比方说,我们想在一个单一对象中包含应用程序的所有配置,而且禁止对该对象进行任何复制或修改。

可以通过对象字面量和类这两种方法来实现:

const Config = {
  start: () => console.log('App has started'),
  update: () => console.log('App has updated'),
}

// 通过冻结对象来限制增加新的属性或者修改已有属性
Object.freeze(Config)

Config.start() // "App has started"
Config.update() // "App has updated"

Config.name = "Robert" // 尝试添加一个新的键
console.log(Config) // 添加失败: { start: [Function: start], update: [Function: update] }

使用对象的字面量

class Config {
    constructor() {}
    start(){ console.log('App has started') }  
    update(){ console.log('App has updated') }
}

const instance = new Config()
Object.freeze(instance)

使用类

工厂方法

工厂方法提供创建对象的接口,对象被创建后可以修改。这样做的好处是,创建对象的逻辑集中在一个地方,这样简化了代码,使得代码更易组织。

这种模式被大量应用。可以通过类和工厂函数(返回对象的函数)来实现:

class Alien {
    constructor (name, phrase) {
        this.name = name
        this.phrase = phrase
        this.species = "alien"
    }
    fly = () => console.log("Zzzzzziiiiiinnnnnggggg!!")
    sayPhrase = () => console.log(this.phrase)
}

const alien1 = new Alien("Ali", "I'm Ali the alien!")
console.log(alien1.name) // 输出:"Ali"

使用类

function Alien(name, phrase) {
    this.name = name
    this.phrase = phrase
    this.species = "alien"
}

Alien.prototype.fly = () => console.log("Zzzzzziiiiiinnnnnggggg!!")
Alien.prototype.sayPhrase = () => console.log(this.phrase)

const alien1 = new Alien("Ali", "I'm Ali the alien!")

console.log(alien1.name) // 输出 "Ali"
console.log(alien1.phrase) // 输出 "I'm Ali the alien!"
alien1.fly() // 输出 "Zzzzzziiiiiinnnnnggggg"

使用工厂函数

抽象工厂

抽象工厂允许在不指定具体类的情况下生成一系列相关的对象。当你想要创建仅共享某些属性和方法的对象时,抽象工厂模式就可以派上用场。

它的工作方式是给客户端提供一个可以交互的抽象工厂。抽象工厂通过特定逻辑调用具体工厂,具体工厂返回最终的对象。

这样做给工厂模式添加了一个抽象层,我们通过仅和单个工厂函数或者类交互来创建各种不同类型的对象。

让我们来看几个例子。假设我们是汽车公司,我们除了生产小汽车以外,还生产摩托车和卡车。

// 每个汽车种类有一个类或者“具体工厂”
class Car {
    constructor () {
        this.name = "Car"
        this.wheels = 4
    }
    turnOn = () => console.log("Chacabúm!!")
}

class Truck {
    constructor () {
        this.name = "Truck"
        this.wheels = 8
    }
    turnOn = () => console.log("RRRRRRRRUUUUUUUUUMMMMMMMMMM!!")
}

class Motorcycle {
    constructor () {
        this.name = "Motorcycle"
        this.wheels = 2
    }
    turnOn = () => console.log("sssssssssssssssssssssssssssssshhhhhhhhhhham!!")
}

// 抽象工厂作为单一交互点和客户端交互
// 接受特定汽车类型作为参数,调用对应类型的具体工厂
const vehicleFactory = {
    createVehicle: function (type) {
        switch (type) {
            case "car":
                return new Car()
            case "truck":
                return new Truck()
            case "motorcycle":
                return new Motorcycle()
            default:
                return null
        }
    }
}

const car = vehicleFactory.createVehicle("car") // Car { turnOn: [Function: turnOn], name: 'Car', wheels: 4 }
const truck = vehicleFactory.createVehicle("truck") // Truck { turnOn: [Function: turnOn], name: 'Truck', wheels: 8 }
const motorcycle = vehicleFactory.createVehicle("motorcycle") // Motorcycle { turnOn: [Function: turnOn], name: 'Motorcycle', wheels: 2 }

构造器

构造器模式分“步骤”创建对象。通常我们通过不同的函数和方法向对象添加属性和方法。

构造器的好处在于通过不同实体分开创建属性和方法。

通过类或者构造函数创建的实例通常继承了所有的属性和方法,但是如果使用构造器,我们可以只应用我们需要的“步骤”来创建对象,这样就更灵活。

这个概念和对象组合相关, 我在这篇‍文章讨论过这个话题。

// 声明一个对象
const bug1 = {
    name: "Buggy McFly",
    phrase: "Your debugger doesn't work with me!"
}

const bug2 = {
    name: "Martiniano Buggland",
    phrase: "Can't touch this! Na na na na..."
}

// 这些函数将对象作为参数,并为对象添加方法
const addFlyingAbility = obj => {
    obj.fly = () => console.log(`Now ${obj.name} can fly!`)
}

const addSpeechAbility = obj => {
    obj.saySmthg = () => console.log(`${obj.name} walks the walk and talks the talk!`)
}

// 最后传入对象作为参数,调用构造器函数
addFlyingAbility(bug1)
bug1.fly() // 输出: "Now Buggy McFly can fly!"

addSpeechAbility(bug2)
bug2.saySmthg() // 输出: "Martiniano Buggland walks the walk and talks the talk!"

原型

原型允许把一个对象作为蓝图创建另一个对象,新对象继承原对象的属性和方法。

如果你已经使用过一段时间的JavaScript,你应该对原型继承有一定了解。

原型链继承的结果和使用类相似,只是更为灵活,因为属性和方法可以不通过同一个类在对象之间共享。

// 声明一个有两个方法的原型对象
const enemy = {
    attack: () => console.log("Pim Pam Pum!"),
    flyAway: () => console.log("Flyyyy like an eagle!")
}

// 声明另外一个对象,这个对象将继承原型
const bug1 = {
    name: "Buggy McFly",
    phrase: "Your debugger doesn't work with me!"
}

// 使用setPrototypeOf设置对象的原型
Object.setPrototypeOf(bug1, enemy)

// 使用getPrototypeOf来确认我们是否设置成功
console.log(Object.getPrototypeOf(bug1)) // { attack: [Function: attack], flyAway: [Function: flyAway] }

console.log(bug1.phrase) // Your debugger doesn't work with me!
console.log(bug1.attack()) // Pim Pam Pum!
console.log(bug1.flyAway()) // Flyyyy like an eagle!

结构范例

结构范例将对象和类组合成更大的结构。

适配器

适配器允许两个接口不兼容的对象相互交互。

假设你的应用程序调用一个API并会返回一个XML,然后将结果发送给另一个API来处理信息,但是处理信息的API期待的是JSON格式。因为格式不兼容,所以你不能直接发送信息,需要先适配结果。

我们可以举一个更简单的例子来具象化这个概念。假设我们有一个以城市为元素的数组,以及一个可以返回拥有最多人口城市的函数。数组中的城市人口以百万为单位计数,但是有一个新城市的人口单位不是百万:

// 城市数组
const citiesHabitantsInMillions = [
    { city: "London", habitants: 8.9 },
    { city: "Rome", habitants: 2.8 },
    { city: "New york", habitants: 8.8 },
    { city: "Paris", habitants: 2.1 },
] 

// 待添加的新城市
const BuenosAires = {
    city: "Buenos Aires",
    habitants: 3100000
}

// 适配器函数将城市的人口属性转换成统一的计数单位
const toMillionsAdapter = city => { city.habitants = parseFloat((city.habitants/1000000).toFixed(1)) }

toMillionsAdapter(BuenosAires)

// 将新城市添加到数组
citiesHabitantsInMillions.push(BuenosAires)

// 函数返回人口最多的城市
const MostHabitantsInMillions = () => {
    return Math.max(...citiesHabitantsInMillions.map(city => city.habitants))
}

console.log(MostHabitantsInMillions()) // 8.9

装饰

装饰通过增加一个修饰对象来包裹原来的对象,从而给原来的对象添加新的行为。如果你熟悉React或者高阶组件(HOC),你内心的小铃铛可能会叮当一下。

从技术上讲,React中的组件是函数而不是对象。但如果你仔细思索React上下文(React Context)或者Memo是怎么运作的,你会发现我们将组件作为子组件传入HOC后,子组件可以访问某些功能。

在下面的例子里中ContextProvider组件接受子组件作为prop:

import { useState } from 'react'
import Context from './Context'

const ContextProvider: React.FC = ({children}) => {

    const [darkModeOn, setDarkModeOn] = useState(true)
    const [englishLanguage, setEnglishLanguage] = useState(true)

    return (
        <Context.Provider value={{
            darkModeOn,
            setDarkModeOn,
            englishLanguage,
            setEnglishLanguage
        }} >
            {children}
        </Context.Provider>
    )
}

export default ContextProvider

然后我们包裹整个应用:

export default function App() {
  return (
    <ContextProvider>
      <Router>

        <ErrorBoundary>
          <Suspense fallback={<></>}>
            <Header />
          </Suspense>

          <Routes>
              <Route path='/' element={<Suspense fallback={<></>}><AboutPage /></Suspense>}/>

              <Route path='/projects' element={<Suspense fallback={<></>}><ProjectsPage /></Suspense>}/>

              <Route path='/projects/helpr' element={<Suspense fallback={<></>}><HelprProject /></Suspense>}/>

              <Route path='/projects/myWebsite' element={<Suspense fallback={<></>}><MyWebsiteProject /></Suspense>}/>

              <Route path='/projects/mixr' element={<Suspense fallback={<></>}><MixrProject /></Suspense>}/>

              <Route path='/projects/shortr' element={<Suspense fallback={<></>}><ShortrProject /></Suspense>}/>

              <Route path='/curriculum' element={<Suspense fallback={<></>}><CurriculumPage /></Suspense>}/>

              <Route path='/blog' element={<Suspense fallback={<></>}><BlogPage /></Suspense>}/>

              <Route path='/contact' element={<Suspense fallback={<></>}><ContactPage /></Suspense>}/>
          </Routes>
        </ErrorBoundary>

      </Router>
    </ContextProvider>
  )
}

接着,我们使用useContext钩子,使得应用内所有组件都可以获得定义在Context的状态(state):

const AboutPage: React.FC = () => {

    const { darkModeOn, englishLanguage } = useContext(Context)

    return (...)
}

export default AboutPage

这个例子可能不是书的作者在写这个模式时想到的确切实现,但我相信想法是一样的:把一个对象放在另一个对象中,这样它就可以访问某些功能。;)

外观

外观模式给库、框架以及其他复杂的类集提供简化的接口。

嗯……我们可以举的例子非常多,不是吗?React本身以及各种各样的软件开发相关的库就是基于这个模式。特别是当你思考声明式编程,会发现这个范式就是使用抽象的方法对开发者隐藏复杂性。

JavaScript中的 mapsortreducefilter函数都是很好的例子,这些函数的背后其实是我们的老朋友for循环。

另一个例子是一些UI库,如:MUI。正如以下示例所展现的这样,库提供了组件,组件带来了内置特性和功能,帮助我们更快、更轻松地构建代码。

这些代码最后都会编译成简单的HTML元素,这是浏览器唯一能理解的东西。组件只是采用了抽象的办法,使得我们的编码过程更容易。

一个外观模式......

import * as React from 'react';
import Table from '@mui/material/Table';
import TableBody from '@mui/material/TableBody';
import TableCell from '@mui/material/TableCell';
import TableContainer from '@mui/material/TableContainer';
import TableHead from '@mui/material/TableHead';
import TableRow from '@mui/material/TableRow';
import Paper from '@mui/material/Paper';

function createData(
  name: string,
  calories: number,
  fat: number,
  carbs: number,
  protein: number,
) {
  return { name, calories, fat, carbs, protein };
}

const rows = [
  createData('Frozen yoghurt', 159, 6.0, 24, 4.0),
  createData('Ice cream sandwich', 237, 9.0, 37, 4.3),
  createData('Eclair', 262, 16.0, 24, 6.0),
  createData('Cupcake', 305, 3.7, 67, 4.3),
  createData('Gingerbread', 356, 16.0, 49, 3.9),
];

export default function BasicTable() {
  return (
    <TableContainer component={Paper}>
      <Table sx={{ minWidth: 650 }} aria-label="simple table">
        <TableHead>
          <TableRow>
            <TableCell>Dessert (100g serving)</TableCell>
            <TableCell align="right">Calories</TableCell>
            <TableCell align="right">Fat&nbsp;(g)</TableCell>
            <TableCell align="right">Carbs&nbsp;(g)</TableCell>
            <TableCell align="right">Protein&nbsp;(g)</TableCell>
          </TableRow>
        </TableHead>
        <TableBody>
          {rows.map((row) => (
            <TableRow
              key={row.name}
              sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
            >
              <TableCell component="th" scope="row">
                {row.name}
              </TableCell>
              <TableCell align="right">{row.calories}</TableCell>
              <TableCell align="right">{row.fat}</TableCell>
              <TableCell align="right">{row.carbs}</TableCell>
              <TableCell align="right">{row.protein}</TableCell>
            </TableRow>
          ))}
        </TableBody>
      </Table>
    </TableContainer>
  );
}

代理

代理模式为另一个对象提供替代或者占位符。这个想法是控制对原始对象的访问,当请求到达实际的原始对象之前或者之后再执行某种操作。

如果你熟悉ExpressJS的话,这个概念就不陌生。Express是用于开发NodeJS API的框架,其中一个功能就是中间件的使用。中间件是我们可以在请求到达终点之前、之中和之后执行的一段代码。

让我们看一个例子。是一个验证身份令牌的函数,不用太关注验证是如何实现的,但是要注意函数接受令牌作为参数,一旦验证完毕就会调用next()函数。

const jwt = require('jsonwebtoken')

module.exports = function authenticateToken(req, res, next) {
    const authHeader = req.headers['authorization']
    const token = authHeader && authHeader.split(' ')[1]

    if (token === null) return res.status(401).send(JSON.stringify('No access token provided'))

    jwt.verify(token, process.env.TOKEN_SECRET, (err, user) => {
      if (err) return res.status(403).send(JSON.stringify('Wrong token provided'))
      req.user = user
      next()
    })
}

这个函数就是一个中间件,我们可以API中的任意终点使用这个中间件。只需要将其添加在终点地址之后,终点的函数声明之前:

router.get('/:jobRecordId', authenticateToken, async (req, res) => {
  try {
    const job = await JobRecord.findOne({_id: req.params.jobRecordId})
    res.status(200).send(job)

  } catch (err) {
    res.status(500).json(err)
  }
})

如果没有提供令牌或者提供了错误的令牌,中间件就会返回相应的错误响应。如果提供了有效令牌,中间件将调用next()函数,然后将执行终点函数。

我们可以在终点内部编写相同的代码来验证令牌,这样就用不着中间件了,但使用了抽象的方法,我们可以在不同的终点复用中间件。

同样这个例子可能不是作者的确切想法,但我相信这是一个有效的例子。我们控制对象的访问,以便我们可以在特定时刻执行操作。

行为范式

行为范式控制不同对象之间的通讯。

责任链

责任链将请求通过处理链传递,链条上的每一个处理程序决定要么处理请求,要么将请求传递给链条上的下一个处理程序。

我们可以使用之前示例来演示这个模式,因为Express的中间件就是一种处理程序,要么处理请求,要么将其传递给下一个处理程序。

如果你想要另一个示例,可以考虑任何需要通过步骤来一步一步实现信息处理的系统。在每个步骤中,不同的实体负责执行操作,并且只有在满足特定条件时,信息才会传递给另一个实体。

需要使用API的前端应用程序就是很好的例子:

  • 有一个负责渲染UI的函数
  • 一旦渲染,另一个函数向API终点发出请求
  • 如果终点响应符合预期,则将信息传递给另一个函数,该函数以给定方式对数据进行排序并存储在变量中
  • 一旦变量存储了所需的信息,另一个函数负责在UI中呈现它。

可以看到这里有许多不同的实体协作执行任务。每个都负责该任务的一个“步骤”,这有助于代码模块化和关注点分离。

迭代器

迭代器用于遍历集合的元素。这在现代编程语言中显得微不足道,但并非如此。

JavaScript内置函数(forforEachfor...offor...inmapreducefilter等)就是手边可以拿来遍历数据结构的方法。

遍历算法以及更为复杂的树和图这样的数据结构使用的代码也是迭代器的例子。

观察者

观察者模式允许你定义一个订阅机制来通知多个对象它们正在观察的对象发生的任何事件。基本上,这就像在给定对象上有一个事件侦听器,当该对象执行我们正在侦听的操作时,我们会采取一些行动。

React的useEffect钩子就是一个很好的例子。useEffect在我们声明的那一刻执行给定的函数。

钩子分为两个主要部分:可执行函数和依赖数组。如果数组为空,如下例所示,每次渲染组件时都会执行该函数。

  useEffect(() => { console.log('The component has rendered') }, [])

如果在依赖数组中声明任何变量,则该函数将仅在这些变量发生变化时执行。

  useEffect(() => { console.log('var1 has changed') }, [var1])

也可以将JavaScript的事件监听器视为观察者模式。另外,响应式编程和库如RxJS,用来处理异步信息和事件的方法也是这个模式。

总结

如果你想了解更多相关信息,推荐观看这个视频或访问这个网站,你可以找到每个模式的配图详细介绍。

希望你享受阅读这篇文章,并有所收获。我们下篇文章见!✌️


本文由哈喽比特于1年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/uruxNqjKQ7YUDIXOA3p4Zw

 相关推荐

刘强东夫妇:“移民美国”传言被驳斥

京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。

发布于:8月以前  |  808次阅读  |  详细内容 »

博主曝三大运营商,将集体采购百万台华为Mate60系列

日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。

发布于:8月以前  |  770次阅读  |  详细内容 »

ASML CEO警告:出口管制不是可行做法,不要“逼迫中国大陆创新”

据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。

发布于:8月以前  |  756次阅读  |  详细内容 »

抖音中长视频App青桃更名抖音精选,字节再发力对抗B站

今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。

发布于:8月以前  |  648次阅读  |  详细内容 »

威马CDO:中国每百户家庭仅17户有车

日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。

发布于:8月以前  |  589次阅读  |  详细内容 »

研究发现维生素 C 等抗氧化剂会刺激癌症生长和转移

近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。

发布于:8月以前  |  449次阅读  |  详细内容 »

苹果据称正引入3D打印技术,用以生产智能手表的钢质底盘

据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。

发布于:8月以前  |  446次阅读  |  详细内容 »

千万级抖音网红秀才账号被封禁

9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...

发布于:8月以前  |  445次阅读  |  详细内容 »

亚马逊股东起诉公司和贝索斯,称其在购买卫星发射服务时忽视了 SpaceX

9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。

发布于:8月以前  |  444次阅读  |  详细内容 »

苹果上线AppsbyApple网站,以推广自家应用程序

据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。

发布于:8月以前  |  442次阅读  |  详细内容 »

特斯拉美国降价引发投资者不满:“这是短期麻醉剂”

特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。

发布于:8月以前  |  441次阅读  |  详细内容 »

光刻机巨头阿斯麦:拿到许可,继续对华出口

据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。

发布于:8月以前  |  437次阅读  |  详细内容 »

马斯克与库克首次隔空合作:为苹果提供卫星服务

近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。

发布于:8月以前  |  430次阅读  |  详细内容 »

𝕏(推特)调整隐私政策,可拿用户发布的信息训练 AI 模型

据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。

发布于:8月以前  |  428次阅读  |  详细内容 »

荣耀CEO谈华为手机回归:替老同事们高兴,对行业也是好事

9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。

发布于:8月以前  |  423次阅读  |  详细内容 »

AI操控无人机能力超越人类冠军

《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。

发布于:8月以前  |  423次阅读  |  详细内容 »

AI生成的蘑菇科普书存在可致命错误

近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。

发布于:8月以前  |  420次阅读  |  详细内容 »

社交媒体平台𝕏计划收集用户生物识别数据与工作教育经历

社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”

发布于:8月以前  |  411次阅读  |  详细内容 »

国产扫地机器人热销欧洲,国产割草机器人抢占欧洲草坪

2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。

发布于:8月以前  |  406次阅读  |  详细内容 »

罗永浩吐槽iPhone15和14不会有区别,除了序列号变了

罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。

发布于:8月以前  |  398次阅读  |  详细内容 »
 相关文章
Java 中验证时间格式的 4 种方法 1年以前  |  3361次阅读
Java经典面试题答案解析(1-80题) 4年以前  |  2686次阅读
IDEA依赖冲突分析神器—Maven Helper 4年以前  |  2467次阅读
CentOS 配置java应用开机自动启动 3年以前  |  2465次阅读
SpringBoot 控制并发登录的人数教程 4年以前  |  2176次阅读
 目录