计算机是人思想的一部分,逻辑、秩序、规则…… 不常见的任务须要额外的编程才能解决。git
机器很死板,无趣。程序员
忍受机器后能享受到便利。express
借鉴了天然界以及人类的模式到编程的设计上。编程
编程的规则原理语法很简单,但创造出知足复杂世界的需求会很难。api
学习中的有些困难和痛苦势必要的。这样以后的学习就更容易了。数组
不要妄自菲薄,换换方式,或者歇一歇,而后坚持学习。浏览器
计算机结构是庞大的,找到围绕目标的所需的知识,理解运行原理。安全
计算机很蠢,可是优势是很快。bash
控制计算机解决问题,就是掌控复杂度的艺术,掌控很差就崩了。。服务器
新的问题每每用旧的眼光和方式解决很差,或者不够好。
不断的实践出错优化才能融会贯通各类状况下的考虑,而简单的阅读却没办法作到。
从最底层到最接近问题,编程语言存在着不一样层面的抽象,有时候细节须要忽略,有时候又要操纵细节,这方面作得最好的是C++,而JS是主要面向浏览器的。它的不少抽象层级都面向浏览器的应用而不是性能,底层,等等。
干同一件事的代码能够写成不少样子,是更易阅读,更省性能仍是写代码用的时间最少(这是人和公司最宝贵的东西,除此以外才是金钱花费),取决于你的需求进行平衡取舍。
JS最先是写浏览器交互、特效之类的小脚本。
JS与Java有关,否则为啥不起别名,但仅仅是为了蹭市场名声,技术实现上上大抵都无关。
JS在编程语言中很烂,但它面向新手,使用的人多了,一次次版本更新也变好起来了。曲线救国,人多才是王道。
JS历史不谈,2015年后ES6版本,每一年都有更新,因此快更新浏览器与时俱进吧!
JS还在MongoDB中能够做为脚本查询语言、Node.js服务器语言。
不只要学,还要写出来。这是一门工程学科,实现是根本,学是为了实现的更好。 Don’t assume you understand them until you’ve actually written a working solution.
语言,浏览器,Node.js。也就是能干什么,在哪里干,还能够在哪里干。
程序的表面之下是庞大的代码支撑着
计算机中只有数据,数据的信息能够被解释为控制指令、文件内容等。
二进制的bit,binary digit,只有0和1的序列,表明着各类信息。
例如数字13的二进制表示。
0 0 0 0 1 1 0 1
128 64 32 16 8 4 2 1
复制代码
易失性内存:8g -> 687 1947 6736 bits,687亿个比特(吓了一跳吧~)
管理如此大量的比特们,须要进行分模块处理,否则会迷失混乱。values, 编程语言中的各类值来划分不一样含义的比特信息,值多了抽象出类型,具备一样的特色,细化下去就是值的不一样,再向上抽象就是类型的不一样。
计算机是复杂的,内存也是复杂的,但人的生命是有限的,没办法了解全部的细节。(这是全部矛盾的源头)
cost
6
let cost = 6;
6
是cost
绑定的值,仍是这行命令执行6
次。6
给我存在哪一个地方6
能够共享给其余变量,仍是不能重复利用的的独一无二的6
进行的绑定……number, 64bits
if((x-3.14)<0.01)
, 而不是 if(x==3.14)
数字通常还要分出一位用来表示正负,若是还要表示小数就要有更多位的损耗,有各类表示小数的方法,你看想要完成更多的功能,就确定要付出一些东西。
数字的主要功能是用来算术,因此这些东西不是被凭空发明出来的,而是实实在在有用才会出如今JavaScript中。
二元操做符:+ - * / %
precedence: * / % > + -
%: remainder operator
改变默认优先级:括号
Infinity
, -Infinity
NaN
: not a numberbackticks
,反引号中的string,也叫template literalhalf of 100 is ${100 / 2}
${}
''
""
\
+
操做符:concatenates操做符除了用符号表示,再多了符号不够用命名关键字表示.
操做符的操做数能够是一个,两个……
一元操做符:
typeof
: return a string of type of the operand符号的一词多义:
-(2-1)
:
-
,unary operator,操做的对象是一个,即2-1
的结果,语义是将一个数置负,-
,binary operator,须要两个操做对象,语义是有两个数,他们想减信息的存储最理想状态是e次方,即2.718,而现实离理想还有段距离,因此目前的计算机是基于二进制的,实际上三进制更理想。不过二进制是第二好的选择,比十进制不知道高到哪里去了。
既然是二进制,就免不了这个二的状态由哪两个表示,计算机说是0和1,实际上在不一样器件里面的表示,有用电平高低的,有用正弦不一样的,程序利用true
和false
,日常咱们说话用有和没有,是或者不是,开或者关,若是是三进制那还包括一种状况,那就是不知
。
比较的结果是个布尔值
字符的比较是按照ASCII码以及Unicode码来的,而不是字典中从A到Z的顺序,注意Unicode字符是ASCII的超集,而后ASCII中的编码在Unicode中大小是相同的,所谓的兼容ASCII,无非是前面多8个0。
只有一种值不等于它本身: NaN
,它的含义就是用来表示无心义的结果,因此他也不和其余无心义结果相等。Oh,shit,难以理解。。
语义:注意它的操做对象是布尔值,不是用来算数的,尽管数字0和1会被变量提高转换成true 和 false。
&&
||
!
precedence: > == > && > ||
一个三元的逻辑操做符:true ? 1 : 2
, ternary, conditional operator, question mark and a colon
三元操做符短路,意味着有些语句不执行。
短路,左边算完了若是返回左边,右边就不验证了,称short-circuit evaluation.
||
左边true就返回左边,false就返回右边。 &&
左边false就返回左边,true就返回右边。
我从未据说过布尔逻辑的处理是这样的,什么叫and运算?左边false,就返回左边对象?为何不是返回false?我要你这个运算何用?这种神奇的功能我本身八辈子都用不到
垃圾语言,奇葩设定,毁我青春,****。
WTF为何不和C++,Java同样?
Examples of expressions that can be converted to false are:
null;
NaN;
0;
empty string ("" or '' or ``);
undefined.
复制代码
||
先把左边转换成布尔值,是个数就返回左边,不是个数返回右边- `0`, `NaN`, `""`, `null`, `undifined`会被算做 `false`
- 其余的全部算做`true`
复制代码
- `console.log('bat||'ant') // -> bat`
复制代码
- `console.log(null||'hi') // -> hi`
复制代码
||
,使之成为备胎。设计上的委曲求全,唉,但是Rust语言不火啊,人们并不须要正确,人们须要兼容稳定能用。
null
undefined
JS老是喜欢能处理你给的各类值,也就是对你很包容,但这对于精确的控制却很差。语言的设计
type coercion
null*8 -> 0
"5" - 1 -> 4字符拼接优先于数值计算。
"5" + 1 -> 51
NaN
若是一旦产生,那么与此相关的结果仍是会NaN
自动类型转换和严格相等
==
,!=
NaN
null
和 undefined
之间为true,和其余为false===
和!==
砖块在高楼中才能体现更大的价值。意思是良禽择木而栖才能发挥更大价值?
可以产生一个值的代码块称之为表达式,expression。字面值,带括号,运算式都是expression
expression之间互相组合、嵌套组成了语义更复杂的expression。
expression是某一小段子句,而语句,statement是一句完整的话。程序就是一列语句们的集合。
最简单的statement是一个expression加上一个分号。(可忽略的分号。。。)
expression产生一个值,而后被周围的代码所利用。
而statement只立足于他本身,除非指定和其余东西有交集。当改变了屏幕上的文字或者改变了计算机内部的一些状态,以至于影响后来的语句执行的结果,这就叫作影响,effects。像1;
这种语句确实改变了了计算机内部的东西,但对于别的代码没有明显的做用。
分号这个东西大部分时间可加可不加,可是你懂的总有意外。有时候不加分号会让会让两行代码变成互相影响的一句statement,为了安全、不找麻烦,仍是加上吧,否则要认识不少哪些是必须加分号的复杂状况。
表达式会产生一个值,好比1+2
,可是产生的结果3
,若是不马上使用它,或找一个空间分给他,它立刻就不见了。
let caught = 5 * 5;
variable or binding
keyword: let
, 声明+赋值
caught
,就是宣布要有光!,而后计算机内存里面就找到一个地方对应着这个caught
.5*5
的结果绑定到了caught
上。caught
,它就会被要么做为空间地址,要么取它的值25
。var
是个历史遗留问题,const
一次声明赋值为常量,鞠躬尽瘁,死然后已。
字母、数字、$
和_
,不能以数字开头,不能用关键字,不能用保留字。
其实就一个原则就行了,字母开头,各类驼峰表明变量、函数、仍是类。也别起什么各类奇葩名字,没事找事。
程序启动的时候,环境就激活了语言自己的一部分绑定,以及提供交互的一些绑定。
默认环境提供的一些变量类型为function.
调用、请求函数的执行,称invoking, calling, applying.
回想数学f(x)=y+1;函数要有输入x,用括号来表示。称arguments, 这个参数可能(x,y),也能够是(x,3,4)
console.log
functionconsole.log 不是一个绑定,它包含了一个句号,对吧,变量名是不容许的,那它是什么呢? console是一个绑定,.log表明检索这个绑定的一个名为log的property。
side effect: 函数的主要做用是用来返回一个值的,那么显示一段文字,弹出对话框叫作反作用。(这是函数定义,不是写代码的目的。。)
因为函数要return一个结果,因此它符合一个expression,也就是能够和另外的expression组合嵌套在一句statement中。console.log(Math.min(1, 3)+1);
prompt中输入的值为string,用Number()转换成number进行计算,其实不用人工转化下一行计算的时候会自动转换类型的。。
原先一条路走到黑,如今变成二选一,而后继续走下面的语句。
if (1 + 1 == 2) console.log("It's true");
通常状况下代码要写的让人看着方便,大部分都要加大括号,除非一行简单的if。
多重嵌套的时候每次当下判断都是二选一,注意合并简化
2^10 (2 to the 10th power)
do while,for 和 while(){}的区别就是语义上的
continue
纯粹为了易读性,一个仍是两个空格、一个仍是多个换行都不影响它的逻辑。但换不换行是有区别的,参考加不加;
的各类规则。
明明有while为何还要发明for呢?由于经常使用啊,由于代码逻辑惊人的类似重复的部分人就愿意把它封装成新东西,轻轻一挥,魔法就实现了,多好。
for和while的区别就是while常常须要一个循环计数器,而且每轮循环都必需要改动这个循环计数器,再加上原本的条件判断,这不就是for啦?
for中不设循环终止判断,就能够把判断挪到循环体中if(){*; *; break;}中,这样的好处是判断完以后能够继续在for的环境中执行一些语句。也就是说若是有if(){break;},考虑是否能够放在for语句头中?
break
若是用原来的机制实现,至关于在原代码前加了个if,而且添加的一个分支只有一个改变循环条件、而且还要抵消for第三个语句(若是在for语句中)。你看现在你想要的,一个break就能够解决了。
continue
表明着跳过这句以后的循环体中的代码,执行for第三部分(若是在for语句中)。而后继续下一次循环
continue
若是用原来机制实现,至关于原代码前添加if,而且添加的一个分支什么都不作,而且还要抵消for第三个语句(若是在for语句中)。
这两个一个是直接跳出全部循环,一个跳出当前这一轮循环。
当一个变量的变化是由原来的本身进行更新的话,能够直接在赋值的时候指定什么操做
counter = counter +1;
// 更简便的自我更新
counter += 1;
// 对于自个人加一减一,还能够缩写
counter ++;
// 在Java中 a++ 和 ++a 实现不同,返回的对象是一份原a对象的拷贝对象,因此值和原a同样,后者返回的是加完的a对象,因此相对于原a值加一。在JS中还不知道啥实现
复制代码
注意default:
和break:
,虽然不少时候工具会自动猜想你的意图,帮你补全一些不严谨的逻辑。
一个多状态的值 --> 多重if的代码的简写模式 --> 就是switch。。经常使用的东西才会被发明。
多对多的怎么办呢?用函数封装一下,多个输入值,每一个值有多个状态,随你所愿的组合判断,
一切都是有缘由的,可是全部的事咱们真的须要知道缘由?
有时候代码并不能层次鲜明的由浅入深,由全局到定位局部信息的导航功能,这个时候须要注释来快速定位一大坨只有一个名字的代码究竟是干什么的。
VS Code
中快捷键Ctrl /
能够快速判断当前是HTML仍是CSS仍是JavaScript仍是JSX代码进行插入相应的注释。
注意多行嵌套没有实现(不是不能实现)交错的处理。以及 //
右边全算做注释
Q1:
#
##
###
####
#####
######
#######
复制代码
A1:
for (let i=0, j=""; i<7; i++) {
j += "#";
console.log(j);
}
复制代码
Q2:
print number 1-100, 3的倍数用Fizz代替,5的倍数用Buzz代替,如果3和5的倍数,用15代替
复制代码
A2:
for(let i=1; i<=100; i++) {
let result = new Array();
let string = "";
let number = i;
if(i%3===0) {
string += "Fizz";
}
if(i%5===0) {
string += "Buzz";
}
result.push(string||number);
for(let i of result) {
console.log(i);
}
}
复制代码
||
的奇葩设定正好帮助返回FizzBuzz或者备胎数字,纳爱斯。Chessboard
# # # #
# # # #
# # # #
# # # #
# # # #
# # # #
# # # #
# # # #
复制代码
A3:
// 若将该功能封装为函数,下面就是输入的参数变量,n阶矩阵,两种要打印的符号
let size = 8;
let symbol1 = " ";
let symbol2 = "#";
// 横向重复的最小单元,要进行O(logn) 替换掉 O(n)的重复字符的复制
let symbolSum = symbol1 + symbol2;
let contentOddAddon = size%2==1?symbol1:"";
let contentOdd = repeatCharBinary(symbolSum, Math.floor(size/2)) + contentOddAddon;
// 求偶数行的内容,由奇数行内容转换,踢掉第一个,再根据最后一个添加适当元素
let contentEvenArray = contentOdd.split("");
contentEvenArray.shift();
contentEvenArray.push((contentEvenArray[contentEvenArray.length-1]==symbol1)?symbol2:symbol1);
let contentEven = contentEvenArray.join("");
// 纵向重复的最小单元,要进行O(logn) 替换掉 O(n)的重复字符的复制
let contentTwoLine = contentOdd + "\n" + contentEven + "\n";
let contentOddLineAddon = size%2===1?contentOdd:"";
let content = repeatCharBinary(contentTwoLine, Math.floor(size/2)) + contentOddLineAddon;
console.log(content);
// 重复某个字符的nlog函数
function repeatCharBinary(char, n) {
// 这个地方须要进行重复计算字符,能够将代码封装为一个函数
// 线性复制char更改成指数级复制
// 将十进制n转换为二进制,除2取余,对应一、二、四、八、16的权重
// 余1则存在,那么将结果加上对应的权重数量的字符串,余0则表示不存在,那就不加这位的字符串。
let tmp="";
while(n!=0) {
if(n%2==1) {
tmp += char;
}
n = parseInt(n/2); //结果从低位算起,余1则存在,结果加之,继续除2余1,算更高一位
// 以前算每一位的权重是否存在,这一行是算若是存在,对应的字符串长多少,每轮要变为更低位字符串的两倍。
char += char;
}
return tmp;
}
复制代码
搞计算机的是天才么?不是的,你们只是站在巨人的肩膀上,用别人生产的砖块垒起本身想要的一面墙。
函数是个很重要的内容。它将一大块代码浓缩成一个名字。这样能够分解一个大问题的庞大代码,变成几个小问题的代码,只抽象出变化的部分,称之为输入,而后输入参数进入一样逻辑的代码中,产生不一样的输出。而仅仅只须要用一个函数名字就能够关联变量和逻辑。
优秀的文章常常经过不一样的词汇乃至精妙绝伦的句子来体现文章的艺术,而编程不一样,它只在意能干成什么事,因此可能不那么动听引人入胜。
编程中的语言就像公式同样枯燥,但若是懂了就明白它的精确和简洁,不一样于文章给人精神上的享受,相似的帐单也很枯燥,但它精确简洁,而且你须要它的时候,他能知足你。
let area = function(x,y) { return x*y;}
复制代码
能够经过绑定到一个变量来重复调用这个函数。
函数功能
做用域只在当前以及子做用域
多级嵌套,lexical scoping
let hi = function() {
};
复制代码
新建变量的函数绑定,要加分号。
函数绑定的名字呢,还能够被绑定为别的东西,因此变量并非函数,他只是个指示器,表明着它可能指向一个数字、字符串、或者是函数等。
声明式的函数标记, 仅仅声明,不进行调用
function hello() {}
复制代码
let hey = (x, y) => {
};
复制代码
省略了函数名,而后直接将输入指向输出。
const square = x => x*x;
复制代码
,
, 方便往后直接添加.let power = (base, exponent=2) => {
let result;
if(exponent===0) {
result = 1;
} else {
result = base * power(base, exponent-1);
}
return result;
};
console.log(
power(0),
power(1),
power(0,3),
power(2,1),
power(2,2),
power(3),
power(100,3),
power(100,100),
);
复制代码
函数执行完会把return的值返还到调用它的地方,而后继续执行接下来的代码。
call stack: 函数调用的栈
给一个函数多个参数,而函数只要一个的时候,它就取一个而且继续执行。
而若是给的函数不够,那么默认undefined
怎么样JS是否是很混蛋?若是程序员无心写错了,他都不报错?
那若是有意写成这样,至关于C++ 中的函数重载。
function minus(a, b) {
if (b === undefined) return -a;
else return a - b;
}
复制代码
哇,好牛逼。。这样居然实现了减法。。一个数就是减本身,两个就是互相减
默认参数, 另外指定了则按指定的,无指定的按默认的
funcition area(redius, PI=3.14) {
}
复制代码
ES2015后改成了正常编程语言的做用域规则。chain scope,子能够看到父,父不可看到子的变量。 ES2015以前只有function有本身的做用域。
ES2015以前定义的var关键字最好用let, const代替。 若是没有关键字直接hi="hi"
,默认是全局做用域
内部做用域能够调用父做用域的变量,那么若是父做用域想调用子做用域呢?
让父函数新建一个子函数,并return这个子函数,子函数能够访问它爹,而后新建一个变量承接父函数,这个变量就绑定到了return回的子函数。
若是这个变量是全局的,那么这个子函数不会回收,因此它爹也不会回收。
一样不用var新建的变量若是绑定到了一个函数,也是全局变量,不会回收。
因此注意垃圾满了。尽可能不须要不要用闭包。
这样引用一个函数实例的一个子函数,称之为一个闭包。
函数调用本身叫作递归,递归要有终止条件return出去.
优雅和速度老是颇有趣的矛盾体。。。更快意味着可能只顾目的,吃相很差看。更好看,可能顾及的就多,妨碍了核心目标。因此以切都是权衡,哪些必需要快不顾长相,快到什么程度?一样相反。
一个程序的项目实现的地方有不少,市场、宣传、技术、组织。而技术之中也有开发效率、机器效率、用户友好、利于维护等一堆互相矛盾的点,要抓住主要的目的,势必就要在其余点上妥协。你不可能用最快的开发速度,写出最省机器资源、最利于维护扩展、最对用户友好的代码。
机器的快速发展,因此程序语言愈来愈高级,库愈来愈顺手,你若是总是担忧这个地方性能很差,那么你想一想你有没有由于自由的呼吸空气而以为浪费,有没有以为本身跑步呼吸了更多的空气而浪费?为何?由于空气与你的呼吸相比很廉价,一样在机器相对于人的时间很廉价。因此那段代码利于你快速理解阅读、而且最快的知足功能,为何要去省下几口空气呢?除非你掉进了水里(机器运行吃力),当空气(性能)真的很重要的时候,再去考虑空气(性能)。
甚至不少你觉的必定要优化的东西根本不值一提, 就像你多呼吸了两口空气同样可有可无.你收货的只有沾沾自喜觉得赚到了, 但你失去的是你最宝贵的生命的几分钟甚至几个小时.
还有不少时候你所谓的优化在优化编译器的专业大师前不值一提, 它甚至原本能够大幅度自动优化的地方,被你用生涩的奇技淫巧仅仅优化了一点.不要自做聪明, 衡量你生命的付出和换来的产出而不只仅是沾沾自喜.
求从1开始, [+5]或者[*3]获得指定数的方法
递归在多分支的时候实现起来比循环更好。
${ variable }
添加变量.let findIt = target => {
function find(current, history) {
let result;
if (current === target) {
result = history;
} else if (current > target) {
result = null;
} else {
result = find(current + 5, `(${history} + 5)`) ||
find(current * 3, `(${history} * 3)`);
}
return result;
}
return find(1, "1");
}
复制代码
个人函数, 进化吧! 通常对函数的需求体如今两方面:
即保证数字为3, 真的是闻所未闻, 用字符串长度来作判断让其一次次的加0直到3位...
function printFarmInventory(cows, chickens) {
let cowString = String(cows);
while (cowString.length < 3) {
cowString = "0" + cowString;
}
console.log(`${cowString} Cows`);
let chickenString = String(chickens);
while (chickenString.length < 3) {
chickenString = "0" + chickenString;
}
console.log(`${chickenString} Chickens`);
}
printFarmInventory(7, 11);
复制代码
当咱们在农场又加了猪以后, 重复的代码块被封装再一个函数里面.
function printZeroPaddedWithLabel(number, label) {
let numberString = String(number);
while (numberString.length < 3) {
numberString = "0" + numberString;
}
console.log(`${numberString} ${label}`);
}
function printFarmInventory(cows, chickens, pigs) {
printZeroPaddedWithLabel(cows, "Cows");
printZeroPaddedWithLabel(chickens, "Chickens");
printZeroPaddedWithLabel(pigs, "Pigs");
}
printFarmInventory(7, 11, 3);
复制代码
函数名字又臭又长, 这里面有几个基本的信息
0
, 这叫封装不变的, 留出变化的接口(函数的输入)一个名字意思精炼小巧的函数, 不只能让人一眼看出它的意义, 还可让别的代码复用这种基础的函数, 想想语言自己带的那些经常使用的函数, 越是通用越是功能单一, 越是能够互相组合成为更强大的功能. 想想键盘上的26个字符, 不只能打出成千上万的英文文章, 还能打出汉字来.靠的就是抽象基本模式,使得记忆负担小, 这叫讨人喜欢, 而后互相组合能打出各类字, 这叫功能强大. 若是是无心义的编码,你要记2k多种没有规律的基本汉字的编码, 不讨人喜欢吧, 虽然功能也强大.
原则: 不要乱优化修改, 除非你明确的须要, 否则一切从简,快,正确. 当你仅仅须要一朵花的时候, 那就不要又设置花盆又设置肥料. 要控制住你的冲动, 你优化的东西可能和你的目的没多大关联, 仅仅是来自基因的倾向性要让你熟悉探索周围环境, 由于你可能没写多少有意义的代码, 却浪费时间改来改去让机器内存下降0.01%?那主要任务怎么办?
function zeroPad(number, width) {
let string = String(number);
while (string.length < width) {
string = "0" + string;
}
return string;
}
function printFarmInventory(cows, chickens, pigs) {
console.log(`${zeroPad(cows, 3)} Cows`);
console.log(`${zeroPad(chickens, 3)} Chickens`);
console.log(`${zeroPad(pigs, 3)} Pigs`);
}
printFarmInventory(7, 16, 3);
复制代码
函数能够用来return一个有用的值, 也能够side effect.
但主要做用在return的值上面的函数, 才能够有用的和其余函数组合.为啥? 由于side effect就在函数内部, 输出的信息无法传给别的函数.
pure function: 不依赖其余代码的side effect, 也不输出本身的side effect. 每次一样的输入, 都会产生一样的输出. 一个纯函数若是在某一个地方正常, 那么它在全部地方都正常.而其余函数可能依赖的环境不一样.
在机器朝向纯数学发展的路上时, 纯函数确定拥有更严谨有效的解决问题的能力, 但... 机器并非纯粹的理想产物,它被现实一代代所创造优化, 因此用面向机器的代码在目前每每更高效, 而纯函数做为高级抽象, 耗费更多的资源.
let min = (x,y) => x>y?y:x;
复制代码
基本:
let isEven = (x) => {
let result;
switch(x) {
case 0:
result = false;
break;
case 1:
result = true;
break;
default:
result = isEven(x>0?x-2:x+2);
break;
}
return result;
};
console.log(isEven(50));
// → true
console.log(isEven(75));
// → false
console.log(isEven(-1));
// → ??
// 这个屡次判断正负的语句能够提早置于外部, 讲这个函数封闭为闭包访问静态变量, 使判断正负只运行一次.
复制代码
let countChar = (string, char) => {
let cnt = 0;
for(let i=0; i<string.length; i++) {
if(string[i] === char)cnt++;
}
return cnt;
}
console.log(countChar("HelHsdfklkjlkhloHi", "H"))
复制代码