前端基础面试题·第三篇——JavaScript(其一)

1.JavaScript数据类型与运算符

数据类型
原始数据类型:
1.Number
2.String
3.Boolean
4.undefined
5.null
6.Symbol
7.bigint

复杂数据类型:
1.Function
2.非函数:
Array: 数组
Object: 对象
Date: 日期
RegExp: 正则
Map: 映射
Set: 集合
WeakMap: 弱映射
WeakSet: 弱集合
FormData: 表单数据

判断数据类型
1.typeof 运算符 ⼀般⽤来 判断基本数据类型 ,如:string,number,boolean,symbol,bigint(es10新
增⼀种基本数据类型bigint),undefined等。返回数据类型的字符串形式
typeof ⽬前能返回string,number,boolean,symbol,bigint,unfined,object,function这⼋种判断类型,但是注意 null 返回的是 Object。

为什么typeof null 是 Object?
因为在JavaScript中,不同的对象都是使⽤⼆进制存储的,如果⼆进制前三位都是0的话,系统会判断为是Object类型,⽽null的⼆进制全是0,⾃然也就判断为Object
扩展:
000 对象 - object
1 整型 - int
010 双精度类型 - double
100字符串 - strig
110布尔类型 - boolean
1000 Symbol类型
1001 bigint类型

2.instanceof 运算符⽤于判断对象的具体类型,⽐如判断⼀个变量是否是数组类型,⽤typeof是不行的,因为typeof返回的是
object,⽽instanceof可以,因为instanceof可以精准判断对象的具体类型

    //⼿写实现
function myInstance(L, R) { //L代表instanceof左边,R代表右边var RP = R.prototypevar LP = L.__proto__while (true) {if (LP == null) {return false}if (LP == RP) {return true}LP = LP.__proto__}
}
console.log(myInstance({}, Object));

3.constructor 属性
与 instanceof 相似,但是对于 instanceof 只能再检测引⽤类型, ⽽ constructor 还可以检测基本类型,因为
constructor是原型对象的属性指向构造函数。

  • null 和 undefined是⽆效的对象,因此是不会有 constructor 存在的,所以⽆法根据 constructor 来判
    断。
  • JS对象的 constructor 是不稳定的,这个主要体现在⾃定义对象上,当开发者重写prototype 后,原有
    的 constructor 会丢失,constructor 会默认为Object
  • 类继承的也会出错,因为 Object 被覆盖了,检测结果就不对了

4.对象原型链判断:Object.prototype.toString.call(这个是判断类型最准的⽅法)
toString 是Object 原型对象上的⼀个⽅法,该⽅法默认返回其调⽤者的具体类型,更严格的讲,是 toString运⾏时this指向的对象类型, 返回的类型格式为[object,xxx],xxx是具体的数据类型 ,其中包括:String,Number,Boolean,undefined,null,Function,Date,Array,RegExp,Error,HTMLDocument… 基本上所有对象的类型都可以通过这个⽅法获取到。

2.undefined 和 undeclared 的区别

Undeclared(未声明) : 当尝试访问尚未使⽤ var、let 或 const 声明的变量时会发⽣这种情况。
Undefined(未定义) : 它发⽣在使⽤ var、let 或 const 声明变量但未赋值时。
在 JavaScript 中,两者很容易被混为⼀谈typeof 对 undefined 和 undeclared 的 都返回 “undefined”。

3.数组

  • 数组的创建方式:
    var arr = new Array(1, 2);var arr = [1, 2];
  • 数组的常用方法:
Array.from() // 浅拷⻉
从类数组对象或者可迭代对象中创建⼀个新的数组实例。 Array.from还可以接受第⼆个参数,作⽤类似于数组的
map⽅法,⽤来对每个元素进⾏处理,将处理后的值放⼊返回的数组。
Array.of() // 创建数组
根据⼀组参数来创建新的数组实例,⽀持任意的参数数量和类型,没有参数时返回 [],当参数只有⼀个的时候,实
际上是指定数组的⻓度。
Array.prototype.concat() // 连接数组
将传⼊的数组或非数组值与原数组合并,返回⼀个新数组,不影响原数组。
copyWithin(target,start,end)
将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。
target(必需):从该位置开始替换数据。如果为负值,表示倒数。
start(可选):从该位置开始读取数据,默认为 0。如果为负值,表示从末尾开始计算。
end(可选):到该位置前停⽌读取数据,默认等于数组⻓度。如果为负值,表示从末尾开始计算。
find() 
⽤于找出第⼀个符合条件的数组成员
参数是⼀个回调函数,接受三个参数依次为当前的值、当前的位置和原数组
findIndex()
findIndex返回第⼀个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1
find和findIndex这两个⽅法都可以接受第⼆个参数,⽤来绑定回调函数的this对象
fill()
还可以接受第⼆个和第三个参数,⽤于指定填充的起始位置和结束位置.
注意,如果填充的类型为对象,则是浅拷⻉.
push() pop() shift() unshift() 
splice(index,howmay,item1,.....,itemX)
index : 必需。添加/删除项⽬位置,使⽤负数可从数组结尾处规定位置。
howmany :必需。要删除项⽬数量。如果设置为 0,则不会删除项⽬。
item1, ..., itemX: 可选。向数组添加新项⽬。
concat()
concat⽅法⽤于连接两个或多个数组。该⽅法将返回⼀个新数组,不影响原数组。
slice()
创建⼀个包含原有数组中⼀个或多个元素的新数组
reduce()
reduce() ⽅法不会改变原始数组。
filter()
将所有元素进⾏判断,将满⾜条件的元素作为⼀个新的数组返回
some()
将所有元素进⾏判断返回⼀个布尔值,如果存在元素都满⾜判断条件,则返回 true,若所有元素都不满⾜判断条
件,则返回 falseevery()
将所有元素进⾏判断返回⼀个布尔值,如果所有元素都满⾜判断条件,则返回 true,否则为 falsejoin()
将数组中的所有元素转换⼀个字符串。
flat()
将数组展平,返回⼀个新数组,原数组不改变。
flatMap()
将数组中的每个元素都执行⼀个提供的函数,该函数返回⼀个新数组。
indexof()
返回数组中第一个符合条件的元素的索引,如果不存在,则返回-1includes()
返回一个布尔值,表示数组中是否包含给定的元素。
forEach()
遍历数组,将数组中的每个元素执行⼀个提供的函数。
map()
将数组中的每个元素都执行⼀个提供的函数,返回由每次函数调⽤结果组成的新数组。
reverse()
将数组中的元素顺序反转。
sort()
对数组中的元素进⾏排序。
toString()
将数组中的元素转换⼀个字符串。

4.数组去重

1.indexOf,filter

    const arr = [1,2,3,2,3,4]const newArr = arr.filter((item,index) => {if(arr.indexOf(item) === index) {return true } else {return false}})

2.Set

    const set = new Set(arr)const newArr = [...set]

3.Map

    const map = new Map()arr.forEach(item => {if(!map.has(item) {map.set(item,true)})})const newArr = [...map.keys()]

4.forEach,includes

    const arr = [1,2,3,4,3,4,5]const newArr = []arr.forEach(item => {if(!newArr.includes(item)) {newArr.push(item)}})

5.reduce

    const newArr = arr.reduce((pre,i) => {if(pre.indexOf(i) === - 1) {pre.push(i)}},[])

6.暴力算法

    const arr = [1,3,2,3,2,3,2,3,]const newArr = []for(let i = 0 ; i < arr.length;i++) {for(let j = 0;j < i;j++) {if(arr[i] !== arr[j]) {newArr.push(arr[i])} else {break}}}

5.数组拍平

1.flat
flat()⽅法⽤于将数组展平,返回⼀个新数组,原数组不改变。需要传入一个参数,这个参数表示要展平的层数

    const arr = [1,2,[3,4,[5,6]]]const newArr = arr.flat(Infinity)

2.toString + replace + split

    const arr = [1,2,[3,4,[5,6]]]const newArr = arr.toString().replace(/(\[|\])/g,'').split(',')

3.toString + replace + JSON.parse

    const arr = [1,2,[3,4,[5,6]]]const newArr = JSON.parse('[' + arr.toString().replace(/(\[|\])/g,'') + ']')

4.递归

    const arr = [1,2,[3,4,[5,6]]]const newArr = []const fn = (arr) => {for(let i = 0;i < arr.length;i++) {if(Array.isArray(arr[i])) {fn(arr[i])} else {newArr.push(arr[i])}}}fn(arr)

5.扩张运算符

    const arr = [1,2,[3,4,[5,6]]]const newArr = [].concat(...arr)

6.ES6新增的数组方法

1.find // 找到符合条件的第一个元素
2.findIndex // 找到符合条件的第一个元素索引
3.includes // 判断数组中是否包含某个元素
4.flat // 数组拍平
5.flatMap // 数组拍平,并且可以指定回调函数
6.Array.at(index) // 返回指定索引的元素
7.Array.from(arrayLike) // 将伪数组或可遍历对象转换为真数组
8.Array.of(element0, element1, …) // 创建一个新的数组实例,并填充元素
9.Array.copyWithin(target, start = 0, end = this.length) // 从数组中拷贝元素到当前数组
10.Array.fill(value, start = 0, end = this.length) // 用指定的值填充数组
11.Array.entries() // 返回一个包含数组中每个索引的键值对的遍历器对象
12.Array.keys() // 返回一个包含数组中每个索引的键的遍历器对象
13.Array.values() // 返回一个包含数组中每个索引的值的遍历器对象
14.Array.sort(compareFunction) // 对数组中的元素进行排序

7.arguments

1.arguments是⼀个类数组对象,它包含函数调⽤时传⼊的参数。
这个对象存在于使用function声明的函数作用域中,并且只存在于函数内部。
这是一个类数组对象,可以进行遍历啊,也可以使使用下标获取元素含有callee,length属性
2.转为数组

    const arr = Array.from(arguments)const arr = [...arguments]
    Array.prototype.slice.call(argument) // Array.prototype.slice这个方法可以在传入一个类数组对象,该函数会调用类属组上的length属性进行遍历截取,因此在后两个参数中不传值则会将整个类数组都复制一遍,生成一个数组返回

3.扩展运算符

    const arr = [...arguments]const arr = [...arguments.callee]   

8.对象

1.遍历对象
可以使用hasOwnProperty 函数来判断对象一个属性是不是存在与对象那个之中
对象也可以使用[]语法访问某一个属性

    for(let key in obj) {} // 使用for...in 运算可以遍历对象的可枚举属性,包括对象继承的属性Object.keys(obj) // 这个方法可以返回对象中的所有可枚举(不包括继承属性)属性组成的一个数组Object.getOwnPropertyNames(obj) // 这个方法可以返回对象中所有的属性,包括不可枚举的属性Object.getOwnPropertySymbols(obj) // 这个方法可以返回对象中所有的Symbol属性组成的数组Reflect.ownKeys(obj) // 这个方法可以返回对象中所有的属性,包括不可枚举的属性和Symbol属性Object.entries(obj) // 这个方法可以返回对象中所有可枚举的属性组成的数组Object.values(obj) // 这个方法可以返回对象中所有可枚举的属性值组成的数组Object.fromEntries(obj) // 这个方法可以将一个键值对组成的数组转换为对象

9.for…in 和 for…of

1.for…in // 遍历对象的可枚举属性,包括对象继承的属性(专门为遍历对象而构建)

2.for…of // 遍历数组,字符串,Map,Set,arguments等可迭代对象

10.原型与原型链

1.原型
每个函数都有一个prototype属性,这个属性是一个指针,指向一个对象,这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。当这个函数作为构造函数创建实例时,该实例的内部[[Prototype]]指针被赋值为构造函数的prototype属性。这样构造函数的原型对象就和实例产生联系,实力就可以调用构造函数原型对象上的属性与方法。例如Array数组实例的绝大多数api都是来自Array.prototype原型对象。prototype有⼀个默认的constructor属性,⽤于记录实例由哪个构造函数创建.constructor,每⼀个原型都有⼀个constructor属性指向关联的构造函数.
2.原型链
每个实例对象都有一个内部属性[[Prototype]],指向构造函数的原型对象。这个原型对象也有一个自己的原型对象[[Prototype]],层层向上直到Object.特殊的 Function 对象,Function 的 proto 指向的是⾃身的 prototype。
⼀切对象都是继承⾃Object对象,Object 对象直接继承根源对象null。
⼀切的函数对象(包括 Object 对象),都是继承⾃ Function 对象。
Object 对象直接继承⾃ Function 对象。
Function对象的proto会指向⾃⼰的原型对象,最终还是继承⾃Object对象。

43.javaScript中的继承方式

1.原型链继承
由于原型链的存在,可以手动修改一个对象的原型,达到继承的目的。这样一来这个对象的原型对象上的属性与方法可以被该对象使用,近似的实现了两个对象之间的继承关系

    // 父对象构造函数function Person(name) {}Person.prototype.name = 'zhangsan'Person.prototype.say = function() {}// 子对象构造函数function Student(name) {}Student.prototype = new Person()const student = new Student()student.name // zhangsan 实现两个对象之间的继承关系
优点:
  1. 原型链继承是JavaScript中实现继承的主要方式,也是最常用的继承方式。
  2. 原型链继承可以共享父对象的原型属性和方法,避免了重复创建对象。
  3. 原型链继承可以实现多继承,即一个子对象可以同时继承多个父对象。
缺点:
  1. 原型链继承的缺点是子对象共享父对象的原型属性和方法,如果父对象的原型属性是一个引用类型,那么子对象修改这个属性,会影响到其他子对象。
  2. 原型链继承的缺点是子对象无法向父对象传递参数,因为子对象的原型是父对象的一个实例,父对象的构造函数在创建子对象的原型时,子对象无法向父对象的构造函数传递参数。
  3. 原型链继承的缺点是子对象无法继承父对象的构造函数,因为子对象的原型是父对象的一个实例。

2.借用构造函数继承
通过利用javascript中的this关键字,在构造函数中调用父构造函数,并且指定父构造函数this指向为当前作用域this,即可以将父构造函数中添加属性与方法的操作在子构造函数中执行一遍,从而间接实现了继承。

    // 父构造函数function Parent (name) {this.name = namethis.say = function () {}}// 子构造函数function Child (name) {Parent.call(this, name)}const child = new Child("zhangsan")child.name // zhangsan 
优点:
  1. 借用构造函数继承可以向子对象传递参数,解决了原型链继承的缺点。
  2. 借用构造函数继承可以实现多继承,即一个子对象可以同时继承多个父对象。
缺点:
  1. ⽅法都在构造函数中 定义,每次创建实例都会创建⼀遍⽅法

3.组合继承
组合继承是JavaScript中实现继承的主要方式,也是最常用的继承方式。
组合继承通过原型链继承父对象的属性和方法,通过借用构造函数继承父对象的方法。

    // 父构造函数function Parent (name) {this.name = namethis.say = function () {}}// 子构造函数function Child (name) {Parent.call(this, name)}Child.prototype = new Parent()const child = new Child("zhangsan")child.name // zhangsan 
优点:
  1. 组合继承可以共享父对象的原型属性和方法,避免了重复创建对象。
  2. 组合继承可以实现多继承,即一个子对象可以同时继承多个父对象。
  3. 组合继承可以向子对象传递参数,解决了原型链继承的缺点。
缺点:
  1. 组合继承的缺点是子对象的原型是父对象的一个实例,父对象的构造函数在创建子对象的原型时,子对象无法继承父对象的构造函数。

4.Class继承
ES6中新增的Class语法糖,通过extends关键字实现继承。

    class Parent {constructor(name) {this.name = name}say() {}}class Child extends Parent {constructor(name) {super(name)}}const child = new Child("zhangsan")child.name // zhangsan 
优点:
  1. Class继承可以共享父对象的原型属性和方法,避免了重复创建对象。
  2. Class继承可以实现多继承,即一个子对象可以同时继承多个父对象。
  3. Class继承可以向子对象传递参数,解决了原型链继承的缺点。
缺点:
  1. Class继承的缺点是子对象的原型是父对象的一个实例,父对象的构造函数在创建子对象的原型时,子对象无法继承父对象的构造函数。

44.new操作符创建一个对象的过程

1.创建一个空对象
new操作符使用会创建一个空对象,并且将这个空对象的原型指向构造函数的prototype属性。
2.修改this指向
3.调用构造函数
4.返回对象

手写一个new操作符

    function newOperator(func,...args) {// 1.创建新对象const obj = {}// 2.将新对象的原型指向构造函数的prototype属性obj.__proto__ = func.prototype// 3.修改this指向并调用构造函数const res = func.apply(obj,args)// 4.返回新对象return typeof res === 'object' ? res : obj}

45.instanceof 操作符

原理: instanceof操作符用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上。原理是判断构造函数的prototype属性是否出现在对象的原型链上
手写一个instanceof操作符

    function instanceOf(left,right) {// 循环判断构造函数是否出现在对象的原型链上while(left !== null) {if(left.__proto__ === right.prototype) {return true}left = left.__proto__}return false}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/52215.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

word中怎么快速选中光标之前或之后的全部内容?

在Word中&#xff0c;快速选中光标之后的全部内容的快捷键&#xff1a;Ctrl Shift End&#xff1b; 在Word中&#xff0c;快速选中光标之前的全部内容的快捷键&#xff1a;Ctrl Shift Home。 在Word中&#xff0c;选取的快捷键如下。 一、选定整个文本&#xff1a; 1&#…

Cozer必备!一站式解锁扣子全网最全插件集锦(五)

俗话说&#xff0c;工欲善其事必先利其器&#xff01; 用过Coze的朋友都知道&#xff0c;插件在Coze里的重要性。插件库就相当于武器库&#xff0c;一个好的插件&#xff0c;就相当于一件趁手的兵器&#xff0c;可以让你事半功倍&#xff01; 程哥精心整理了Coze最常用和好用…

spring cloud openFeign

1:Intellij 新建 项目 order-service 2:pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation&q…

golang学习笔记02——gin框架及基本原理

目录 1.前言2.必要的知识3.路由注册流程3.1 核心数据结构3.2 执行流程3.3 创建并初始化gin.Engine3.4 注册middleware3.5 注册路由及处理函数&#xff08;1&#xff09;拼接完整的路径参数&#xff08;2&#xff09;组合处理函数链&#xff08;3&#xff09;注册完成路径及处理…

精通Java微服务

第1章 微服务是在面向服务架构SOA的基础上进一步发展而来的&#xff0c;它比SOA做得更加彻底&#xff0c;其单体服务被更加彻底地划分&#xff0c;最大限度地实现了服务的单一职责。 1.1.2互联网 即计算机网络&#xff0c;连接了世界上数以万计的计算机设备&#xff08;可联…

15、Django Admin添加自定义字段功能

修改模型类HeroAdmin admin.register(Hero) class HeroAdmin(admin.ModelAdmin):change_list_template "entities/heroes_changelist.html"... # 此处原代码不动&#xff0c;只增加此前后代码def get_urls(self):urls super().get_urls()my_urls [path(immort…

最新版 | SpringBoot3如何自定义starter(面试常考)

文章目录 一、自定义starter介绍二、自定义Starter的好处及优势三、自定义starter应用场景四、自定义starter1、创建autoconfigure的maven工程2、创建starter的maven工程3、在autoconfigure的pom文件中引入MyBatis的所需依赖4、编写自动配置类MyBatisAutoConfiguration5、编写i…

pdf文件编辑器有哪些?分享适合新手用的5个PDF编辑器(解锁教程)

pdf是一种通用文件格式&#xff0c;也是一种夸操作系统平台的文件格式。 好用的PDF文件编辑器可以让您更改和添加文本、编辑图像、添加图形、签署签名、填写表单数据等。下面整理了关于pdf文件编辑方法介绍&#xff0c;以及一些好用的pdf编辑器&#xff0c;有需要的可以了解下…

JavaScript初级——Navigatior

1、Navigator 代表当前浏览器的信息&#xff0c;通过该对象可以来识别不同的浏览器。 2、由于历史原因&#xff0c;Navigator 对象中的大部分属性已经不能帮助我们是被浏览器了。 3、一般使用 userAgent 来判断浏览器的信息。 4、 userAgent 是一个字符串&#xff0c;包含有用来…

C# 通过拖控件移动窗体

目录 引言一、通过控件事件移动窗体1、创建窗体界面2、添加控件事件3、添加代码 二、通过windowsAPI移动窗体1、 构建窗体和添加事件2、代码展示 引言 在C#Form窗体设计中&#xff0c;如果我们不需要使用默认边框设计自己个性化的窗体&#xff08;FromBorderStylenone时&#…

LEAN 类型理论之注解(Annotations of LEAN Type Theory)-- 商类型(Quotient Type)

商类型&#xff08;Quotient Type&#xff09;&#xff0c;也称划分类型&#xff0c;通过给定义一个定义在某一类型 α 上的关系R&#xff1a;α → α→ ℙ&#xff0c;将类型α 中&#xff0c;满足关系R的元素摘出来&#xff0c;组成该商类型&#xff08;Quotient&#xff09…

2024国赛数学建模C题完整论文:农作物的种植策略

农作物种植策略优化的数学建模研究&#xff08;完整论文&#xff0c;持续更新&#xff0c;大家持续关注&#xff0c;更新见文末名片 &#xff09; 摘要 在本文中&#xff0c;建立了基于整数规划、动态规划、马尔科夫决策过程、不确定性建模、多目标优化、相关性分析、蒙特卡洛…

20:HAL--RNG

一&#xff1a;RNG 伪随机数就是rand函数产生的数。 F1系列的没有RNG,使用本次说的都是F407的&#xff0c;我使用的是STM32F407VET6 B&#xff1a;框架 C&#xff1a;寄存器 /*RNG_SR寄存器的DRDY位*/ while ((__HAL_RNG_GET_FLAG(&rng_handle, RNG_FLAG_DRDY) RESET) …

Oracle start with connect BY 死循环

解决办法 检查start with前有没有where条件&#xff0c; 如果有的话&#xff0c;套一层select&#xff0c;再 Oracle start with connect BY

Opencv中的直方图(1)计算反向投影直方图函数calcBackProject()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 计算直方图的反向投影。 cv::calcBackProject 函数计算直方图的反向投影。也就是说&#xff0c;类似于 calcHist&#xff0c;在每个位置 (x, y)…

PostgreSQL的repmgr工具介绍

PostgreSQL的repmgr工具介绍 repmgr&#xff08;Replication Manager&#xff09;是一个专为 PostgreSQL 设计的开源工具&#xff0c;用于管理和监控 PostgreSQL 的流复制及实现高可用性。它提供了一组工具和实用程序&#xff0c;简化了 PostgreSQL 复制集群的配置、维护和故障…

CSS解析:定位和层叠上下文

许多开发人员对定位的理解很粗略&#xff0c;如果不完全了解定位&#xff0c;就很容易给自己挖坑。有时候可能会把错误的元素放在其他元素前面&#xff0c;要解决这个问题却没有那么简单。 一般的布局方法是用各种操作来控制文档流的行为。定位则不同&#xff1a;它将元素彻底…

虚幻地形高度图生成及测试

虚幻地形高度图生成及测试 虚幻引擎地形系统将高度数据存储在高度图中&#xff0c;这是一个灰阶图像&#xff0c;使用黑白色值来存储地貌高程。在高度图中&#xff0c;纯黑色值表示最低点&#xff0c;纯白色值表示最高点。支持16位灰阶PNG、8位灰阶r8及16位灰阶r16格式。 本文…

华为 HCIP-Datacom H12-821 题库 (8)

有需要题库的可以看主页置顶 1.在 DHCP 运行过程中&#xff0c;如果客户端 IP 地址在相约过去 87.5%还没有完成续约的话&#xff0c;客户将发送什么报文进行再次续约&#xff1f; A、DHCP discover 广播报文 B、DHCP release 单播报文 C、DHCP request 广播报文 D、DHCP reques…

硬刚苹果还得是华为

文&#xff5c;琥珀食酒社 作者 | 璇子 牛皮啊 华为发三折叠不意外 意外的是 这各种翻转简直颠覆想象 市面上没见过这么能“翻转”的&#xff1f; 要不怎么说硬刚苹果 还得看华为 就跟你同天怎么了&#xff1f; 拼创新、拼技术、拼热度 你就说哪比你差吧&#xff1f…