运行在客户端(浏览器)的编程语言,实现人机交互效果,而html与css是标记语言
javascript作用:
- 网页特效(监听用户的一些行为,让网页作出对应的反馈)
- 表单验证(针对表单数据的合法性进行判断)
- 数据交互(获取后台的数据,渲染到前端)
- 服务端编程(node.js)
javascript组成:
- ECMAScript语言基础
- Web APIs(DOM文档对象模型、BOM浏览器对象模型)
javascript权威网站:MDN
javascript书写位置 内部js:
- 内部式:用script标签包住,由于顺序加载,放在底部是最好的策略
- 外部式:通过script标签,引入到html页面中,其script标签内不允许写代码,会被忽略
- 内联是:在标签中直接书写js表达式
javascript注释:
- 单行注释:// ctrl + /
- 多行注释:/* * / shift + alt + a
javascript结束符:
- 可以加;
- 也可以不加;
javascript输入和输出语法:
- 文档输出内容 document.write(‘
我是标题
’)- 弹出警告对话框 alert(‘警告’)
- 控制台输出 console.log(‘前端’)
- 输入语句 prompt(‘请输入你的年龄:’)
javascript代码执行顺序:
- 按HTML文档顺序执行js代码
- alert( )和prompt( )会跳过页面渲染先被执行
javascript字面量: 在计算机科学中,字面量(literal)是在计算机中描 事/物
变量
变量:计算机中用来存储数据的“容器”
【注意】变量本身不是数据,仅仅是一个用于存储数值的容器
变量的命名和规范:
- 不能使用关键字
- 只能由数字、字母、下划线、$组成,且不能以数字开头
- 字母严格区分大小写额,如Age和age是不同的变量
- 起名要有意义
- 遵循小驼峰命名法
变量的声明:var、let(ES6)、const(ES6)(面试常问!!!)
var:
- 存在变量提升,但只是声明提升,不是赋值提升,提前调用输出undifined
- 可以重复声明
- 没有块级作用域
- 成为window对象属性(全局作用域中)
- 没有暂时性死区(声明前可用)
【注意】在函数中使用var声明的变量为局部变量,否则为全局变量
在函数外使用var声明的变量为全局变量,不使用也为全局变量
同为全局变量,同为window对象中属性,使用var声明的变量不可删除,不使用可以删除(delete a)
let:
- 没有变量提升
- 不能重复声明
- 有块级作用域
const:
- 没有变量提升, 一般定义常量,但非空数组和非空对象也可以(即内容追加)
- 不能重复声明,必须初始化,之后不允许修改
- 有块级作用域
数组
let arr = ['吃饭','睡觉','打豆豆']
常量
const PI = 3.14159
const arr = ['吃饭','睡觉','打豆豆']
const obj = { }
基本数据类型:Number数字型、String字符串型、Boolean布尔型、Undefined未定义型、Null空型、Symbol唯一值型
引用数据类型:Object对象型、Array数组型、Function函数型、RegExp正则型、Date日期型
使用表单、prompt获取过来的数据默认是字符串类型的,不能进行简单加法运算,需要类型转换(面试常问!!!)
主要是指转换为Number,String,Boolean,转换为Null和Undefined没有意义
console.log(typeof(true+"123")) [string true123]
console.log(typeof(+"123")) [number 123] 或 console.log(typeof +"123") [number 123]
console.log(+"123pink") NaN 含有非数字
console.log(typeof(true + undefined)) [number] 其值为NaN
console.log(typeof(true - "123")) [number -122]
console.log(typeof(true * "123")) [number 123]
console.log(typeof(true / "123")) [number 0.0081300813]
赋值运算符:对变量进行赋值的运算符
+ += -= *= /= %=
自增运算符:(笔试选择题!!!)
a++ ++a a-- --a
let i = 1
console.log(true + 9 + i++ - ++i * --i / i--)
输出结果为8
比较运算符:
> >= < <= == === !== !===
==进行判断时会进行类型转换,而===进行比较时不会进行类型转换
console.log(undefined == null) [true undifined衍生至null]
console.log(NaN == NaN) [false]
console.log(NaN === NaN) [false]
console.log(isNaN(NaN)) [true]
逻辑运算符:
!:非
!可以对一个值进行非运算,布尔值取反操作,true变成false,如果对非布尔值进行非运算,会先将非布尔值变成布尔值,再进行非操作
可以利用!!a将任意数据类型a转换为Boolean类型,原理和Boolean( )函数一样
&&:与
&&对两侧的值进行与运算并返回结果,只有两侧全为true,结果才返回true,否则false
JS中&&属于短路与,即只要第一个值为false,则为false,不会检查第二个值
||:或
||对两侧的值进行或运算并返回结果,只要有一侧值为true,结果返回true,否则false
JS中||属于短路或,即只要第一个值为true,则为true,不会检查第二个值
案例1:
let a = 3
if(0 < a < 2){
console.log(a,"###")
}
if(0 < a && a < 2){
console.log(a,"@@@")
}
console.log(5 > 3 && 20)
console.log(5 < 3 && 20)
- if 分支语句、三元运算符、switch 分支语句
- while循环局域、for循环语句
switch使用注意:switch(xxx)和case yyy ,会进行===全等比较,相同值和类型才会判断正确
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>猜数字title>
<style>
style>
head>
<body>
<script>
// 1.随机生成1-100数字
let ran = Math.round(Math.random()*(100-1)+1)
// 2.用户输入数字
let num = +prompt("请猜一个1-100之间的整数:")
// 3.判断数字大小给予提示
switch (true){
case num < ran:
alert("猜小了哦~,请继续猜")
break
case num > ran:
alert("猜大了哦~,请继续猜")
break
case num == ran:
alert("恭喜你,猜对了~")
break
}
script>
body>
html>
案例,判断闰年: 能够被4整除,但不能被100整数,或者能够被400整数的年份是闰年
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<style>
style>
head>
<body>
<script>
let year = +prompt('请输入年份:')
if(year % 4 ===0 && year % 100 !==0 || year % 400 ===0){
alert(`你输入的年份${year}是闰年`)
}else{
alert(`你输入的年份${year}不是闰年`)
}
script>
body>
html>
break和continue区别:
break关键字可以用来退出switch或循环语句
不能单独在if语句中使用break和continue
break关键字,会立即终止离它最近的循环语句(终止这个循环) ,不再接着循环判断
continue关键字,会立即终止离它最近的循环语句(终止本次循环),且本次循环下面语句不再执行,再接着循环判断
可以为循环语句创建一个标签,来标识当前循环,使得break/continune可以结束指定标签的循环
outer:for(var i = 0;i<=5;i++){
console.log("外循环"+i);
for(var j=0;j<=5;j++){
console.log("内循环"+j);
break outer;
}
}
创建数组:
- 使用数组构造函数,例如:
const arr = new Array()
- 使用字面量来创建数组,更加简单方便,例如:
const arr = [ ]
字面量和构造函数创建并赋值一个值时两者区别:
前者是创建一个长度为该值的数组const arr = new Array(4) 输出: [<4 empty items>]
后者是创建一个只含一个元素数组const arr = [4] 输出:[4]
数组中的元素可以是任意的基本数据类型,也可以是对象,函数,数组等引用数据类型
(面试常问、笔试常考):
const arr = []
参数中[ ]表示可选
向数组末尾添加一个或多个元素,并返回数组的新长度
该方法会将数组新的长度作为返回值返回
原数组改变
//arr = []
let length = arr.push(1)
console.log(arr) //输出:[1]
console.log(length) //输出:1
删除数组的最后一个元素,并将删除的元素作为返回值返回
原数组改变
//arr = [1]
let element = arr.pop()
console.log(element) //输出:1
console.log(arr) //输出:[]
向数组开头添加一个或多个元素,并返回新的数组长度
原数组改变
//arr = []
let len = arr.unshift(1,2,3,4)
console.log(len) //输出:4
console.log(arr) //输出:[1,2,3,4]
删除数组第一个元素,并将被删除的元素作为返回值返回
原数组改变
//arr = [1,2,3,4]
let ele = arr.shift()
console.log(ele) //输出:1
console.log(arr) //输出:[2,3,4]
从数组中提取指定元素,可以传递一个负值,表示从后面开始提取
参数:截取开始的位置索引,包含开始索引;截取结束的位置索引,不包含结束索引
第二个参数可以省略不写,此时会截取从开始索引往后的所有元素
不改变原数组
//arr = [2,3,4]
let arrSlice = arr.slice(-3,-1)
console.log(arrSlice) //输出:[2,3]
console.log(arr) //输出:[2,3,4]
删除元素并向数组添加新元素,指定元素会将原数组中删除并返回被删除元素
参数:删除开始的位置索引,包含开始索引;删除元素的数量;插入新元素到开始位置索引前面
原数组改变
//arr = [2,3,4]
let arrSplice1 = arr.splice(1,2)
console.log(arrSplice1) //输出:[3,4]
let arrSplice2 = arr.splice(0,1,66,77,88)
console.log(arrSplice2) //输出:[2]
console.log(arr) //输出:[66,77,88]
按顺序连接两个或多个数组,并将新数组返回
不改变原数组
//arr = [66,77,88]
let arrConcat = arr.concat(arrSplice1,arrSplice2)
console.log(arrConcat) //输出:[66,77,88,3,4,2]
console.log(arr) //输出:[66,77,88]
可以将数组转换为字符串并返回字符串
可以指定一个字符串作为参数,成为数组中元素的连接符,默认逗号
不会改变原数组
//arr = [66,77,88]
let arrJoin = arr.join("-")
console.log(arrJoin) //输出:66-77-88 (string类型)
console.log(arr) //输出:[66,77,88]
颠倒数组中元素的顺序
会改变原数组
//arr = [66,77,88]
let arrReverse = arr.reverse()
console.log(arrReverse) //输出:[88,77,66]
console.log(arr) //输出:[88,77,66]
对数组中元素进行排序,默认按照Unicode编码进行排序,会改变原数组
对数字进行排序,可能得到错误的结果
const array = [2,3,12,5,87,6]; console.log(array.sort()); 输出:[12,2,3,5,6,87]
在sort( )中添加回调函数并传递两个参数,可指定排序规则
浏览器根据回调函数返回值决定元素的顺序
如果返回一个大于0的值,则元素会交换位置
如果返回一个小于0的值,则元素位置不变
如果返回一个等于0的值,则元素相等位置不变
例如:升序排列a-b,降序排列b-a
在查看js源码后,V8 引擎 sort 函数只给出了两种排序 InsertionSort 和 QuickSort
数量小于10的数组使用 InsertionSort
比10大的数组则使用 QuickSort
//arr = [66,77,88]
let arrSort = arr.sort((a,b)=>{
return b - a
})
console.log(arrSort) //输出:[88,77,66]
console.log(arr) //输出:[88,77,66]
将数组转为字符串
不改变原数组
const array = [2,true,"12","张三",null,undefined]
let arrToString = array.toString()
console.log(arrToString) //输出:2,true,12,张三,, (string类型)
console.log(arrToString[10]) //输出:张
console.log(array) //输出:[2,true,"12","张三",null,undefined]
从具有 length 属性或可迭代对象的任何对象返回 Array 对象
不改变原对象
var myArr = Array.from("ABCDEFG");
console.log(myArr)
确定数组是否包含指定的元素,包含返回true,不包含返回false
let flag = array.includes(true)
console.log(flag)
const array = [1,true,"张三",undefined,null,{}]
for(let i=0;i<array.length;i++){
console.log(array[i])
}
按顺序为数组中的每个元素调用一次函数
array.forEach((item,index,array) => {
console.log(index,item,array)
});
按顺序为数组中的每个元素调用一次提供的函数,返回一个数组
不改变原数组
const arr = array.map((value,index,array)=>{
return `${index}-${value}`
})
console.log(arr)
const numbers = [4, 9, 16, 25];
const num = numbers.map(Math.sqrt)
console.log(num)
console.log(numbers)
ES6新增,遍历可迭代对象
for (let item of array) {
console.log(item)
}
填充了所有通过测试的数组元素
不改变原数组
const arr = array.filter(value=>{
return value > 0
})
console.log(arr)
console.log(array)
检查数组中的所有元素是否都通过了测试
不改变原数组
const arr = array.every(value=>{
return value > 0
})
console.log(arr)
console.log(array)
检查数组中的任何元素是否通过测试
不改变原数组
const arr = array.some(value=>{
return value > 0
})
console.log(arr)
console.log(array)
可同时将前面数组项遍历产生的结果与当前遍历项进行运算,其它不行
数组中的每个值(从左到右)开始缩减,最终为一个值
不改变原数组
const arr = [6,9,13,15,339]
const result = arr.reduce((sum,value)=>{
console.log(sum)
return sum + Number(value)
})
console.log(result)
console.log(arr)
可同时将前面数组项遍历产生的结果与当前遍历项进行运算,其它不行
数组中的每个值(从右到左)开始缩减,最终为一个值
不改变原数组
const arr = [6,9,13,15,339]
const result = arr.reduceRight((sum,value)=>{
console.log(sum)
return sum + Number(value)
})
console.log(result)
console.log(arr)
返回数组中第一个通过测试的元素的值
不改变原数组
const arr = array.find(value=>{
return value > 0
})
console.log(arr)
console.log(array)
返回数组中通过测试的第一个元素的索引
不改变原数组
const arr = array.findIndex(value=>{
return value > 0
})
console.log(arr)
console.log(array)
ES6提供新的方法,用于遍历数组,返回遍历器对象,可以用for…of进行遍历
entries( )是对键值对的遍历,keys( )是对键名的遍历,values( )是对键值的遍历
for (let [index,item] of array.entries()) {
console.log(index,item)
}
for (let index of array.keys()) {
console.log(index)
}
for (let value of array.values()) {
console.log(value)
}
console.log(array)
创建函数对象:
方式一:构造函数
语法:函数对象( ) 如:var fun = new Function( );
可以将要封装的代码以字符串的形式传递给构造函数
封装到函数中的代码不会立即执行,在调用时按顺序执行代码
实际开发中很少使用构造函数来创建一个对象
方式二:函数声明
语法:function 函数名([形参1,形参2…,形参n]){语句… }
如:function fun([形参1,形参2…,形参n]){ 语句…}
封装到函数中的代码不会立即执行,在调用时按顺序执行代码
方式三:函数表达式
语法:var 函数名 = function([形参1,形参2…,形参n]){语句… };
如:var fun = function([形参1,形参2…,形参n]){语句… };
调用函数时解析器不会检查实参的类型,所以要注意是否有可能接收到非法的参数
调用函数时不会检查实参的数量
多余实参不会被赋值
如果实参的数量少于形参的数量,则没有对应实参的形参将会是undifined
在函数return后的语句都不会执行
如果return语句后不跟任何值就相当于返回一个undifined
如果函数中不写return,则也会返回undifined
作用域:指一个变量的作用范围
全局作用域:直接编写在script标签中的JS代码或一个独立的js文件
在页面打开时创建,在页面关闭时销毁
全局对象window可以直接在页面中调用
创建的变量都会作为window对象的属性保存
创建的函数都会作为window对象的方法保存
创建的全局变量在页面所有位置都可以被调用
函数作用域(局部作用域) :
调用函数时创建,函数执行完毕后,函数作用域被销毁
每调用一次就会创建一次函数作用域,它们之间相互独立
函数作用域中可以访问到全局作用域的变量
全局作用域中无法访问到局部作用域的变量
在函数作用域调用变量时先在自身作用域范围查找变量,否则上一级查找
想在函数作用域中访问全局变量可以使用window对象
函数作用域中也有使用var声明提前的特性,函数作用域中也有函数声明提前的特性
在函数中不使用var声明的变量都会成为全局变量,相当于使用window.变量(没有传参时)
定义形参就相当于在函数作用域中声明了变量
声明提前:
变量的声明提前:
使用var关键字声明的变量会在所有代码执行之前被声明(但是不会被赋值),此时输出:undefined
函数的声明提前:
使用函数声明形式创建的函数:function 函数名( ){ },会在所有代码执行之前被创建,因此可以在函数声明前调用,此时输出:[Function: add]
利用函数表达式创建的函数,不会被声明提前,因此不能在声明前调用
创建对象:
const obj = new Object( ); obj.xxx = “xxx”;
const obj = { 属性名:属性值,属性名:属性值…}
对象的分类:
内建对象:由ES标准中定义的对象,在任何ES的实现中都可以使用>
例如:Math,String,Number,Boolean,Function Object …
宿主对象:由JS的运行环境提供的对象,目前来讲主要由浏览器提供的对象
例如:BOM,DOM
自定义对象:由开发人员自己创建的对象
可以通过in运算符来检查一个对象中是否含有指定的属性,如果有返回true,没有返回false
语法:”属性名” in 对象
const obj = {
name:'zs'
}
console.log("name" in obj) //true
对象原型上的属性也会被循环出来
可以使用对象的hasOwnProperty(xxx)
方法过滤掉原型链上的属性
不会返回对象的不可枚举属性
const obj = {
name:'zs',
age:18,
address:'中国'
}
for(let k in obj){
console.log(k,obj[k]) //string类型 string类型
}
该方法返回对象自身属性名组成的数组,它会自动过滤掉原型链上的属性
不会返回对象的不可枚举属性
const obj = {
name:'zs',
age:18,
address:'中国'
}
Object.keys(obj).forEach(key=>{
console.log(key)
})
该方法返回对象自身属性值组成的数组,它会自动过滤掉原型链上的属性
不会返回对象的不可枚举属性
const obj = {
name:'zs',
age:18,
address:'中国'
}
Object.values(obj).forEach(value=>{
console.log(value)
})
该方法返回对象自身属性名和属性值组成的数组,它会自动过滤掉原型链上的属性
不会返回对象的不可枚举属性
const obj = {
name:'zs',
age:18,
address:'中国'
}
Object.entries(obj).forEach(entry=>{
console.log(entry)
})
返回对象所有属性,包括不可枚举属性
返回对象内所有Symbol属性
返回对象所有属性,无论是否可枚举,或Symbol,或继承属性
Math和其它对象不同,它不是一个构造函数
它属于一个工具类,不用创建对象,里面封装了运算符相关的属性和方法
abs( ):返回一个数的绝对值
ceil( ):对数进行向上取整
floor( ):对数进行向下取整
round( ):对数进行四舍五入取整
random( ):产生0-1之间的随机数,不包括0和1
例如生成[0,x]之间随机整数:Math.round(Math.random( )*x);
生成[x,y]之间随机整数:Math.round(Math.random( )*(y-x)+x);
max( ):获取多个数中的最大值
min( ):获取多个数中的最小值
pow( ):获取x的y次幂
查看Math对象更多属性和方法
(面试题:生成1000个10-10000的随机整数,并统计个数)
const arr = []
for(let i=0;i<1000;i++){
arr.unshift(Math.round(Math.random()*(10000-10)+10))
}
console.log(arr)
function fun(arr) {
const obj = {};
for (let i = 0; i < arr.length; i++) {
let t = arr[i];
// if (obj.hasOwnProperty(t)){ //判断t属性是否为对象obj中的属性;
if(t in obj){
obj[t] = obj[t]+1;
}else{
obj[t] = 1;
}
}
return obj;
}
console.log(fun(arr));
也可以使用reduce函数,更高级,简介一些
const arr = []
for(let i=0;i<1000;i++){
arr.unshift(Math.round(Math.random()*(10000-10)+10))
}
console.log(arr)
/*reduce第一个参数为数组中每个元素运行的函数,
obj为初始值或先前返回的值,currentValue为当前元素的值
第二个参数为传递给函数的初始值【可选】*/
var objGroup = arr.reduce(function (obj, currentValue) {
console.log(obj)
obj[currentValue] = obj[currentValue] ? ++obj[currentValue] : 1;
return obj;
}, {});
console.log(objGroup);
JS中使用Date对象来表示一个时间
如果直接使用构造函数创建一个Date对象,则会封装当前代码执行时间
创建一个指定时间的对象,需要在构造函数中传递一个表示时间的字符串作为参数
日期格式:月份/日/年 时:分:秒
getDate( ):获取当前日期对象日的信息
getDay( ):获取当前日期对象是周几,0表示周日
getMonth( ):获取当前日期对象月的信息,0表示一月
getTime( ):返回 1970 年 1 月 1 日至今的毫秒数,表示时间戳
可以当前时间戳来测试代码执行时间,例如:var time = Date.now( );
查看Date对象其它属性和方法
在底层字符串以字符数组的形式保存的
属性length:获取字符串的长度
返回字符串中指定位置的字符,根据索引获取指定的字符
不改变原字符串
let str = "Hello, World!";
console.log(str.charAt(0)); // H
获取字符串中在指定的位置的字符的 Unicode 编码
不改变原字符串
let str = "Hello, World!";
console.log(str.charCodeAt(0)); // 72
将Unicode 编码转为字符,使用String对象调用
不改变原字符串
let str = "Hello, World!";
console.log(String.fromCharCode(65)); // A
连接两个或多个字符串,作用和+一样
不改变原字符串
let str = "Hello, World!";
console.log(str.concat("hi")); // Hello, World!hi
console.log(str + "hi"); // Hello, World!hi
可以检索一个字符串中是否含有指定内容,有则返回第一次出现的索引,没有则返回-1,
可以指定第二个参数,表示开始查找的位置
不改变原字符串
let str = "Hello, World!";
console.log(str.indexOf("l",4)); // 10
与indexof方法使用类似,但为从后往前开始
可以指定第二个参数,表示开始查找的位置
不改变原字符串
let str = "Hello, World!";
console.log(str.lastIndexOf("o",4)); // 4
可以从字符串中截取指定内容,第一个参数开始位置的索引,包括开始位置
第二个参数结束位置的索引,不包括结束位置
传递负数作为参数,表示后面位置
不改变原字符串
let str = "Hello, World!";
console.log(str.slice(2,-2)); // llo, Worl
提取字符串中两个指定的索引号之间的字符,包括开始,不包括结束
该方法不能接收负值作为参数,负值作为0
若第二个参数小于第一个参数则会自动交换位置
不改变原字符串
let str = "Hello, World!";
console.log(str.substring(4,-2)); // Hell
用来截取字符串,第一个参数表示截取开始位置的索引,第二个参数表示截取的长度
不改变原字符串
let str = "Hello, World!";
console.log(str.substr(1,3)); // ell
按照某个字符把一个字符串分割成字符串数组,第二个参数表示返回子串长度不会多于该值
传入""表示每个字符之间都被会分割
不改变原字符串
let str = "Hello, World!";
console.log(str.split("l",3)); // [ 'He', '', 'o, Wor' ]
把字符串转换为小写,会改变原数组
不改变原字符串
let str = "Hello, World!";
console.log(str.toLowerCase()); // hello, world!
把字符串转换为大写,会改变原数组
不改变原字符串
let str = "Hello, World!";
console.log(str.toUpperCase()); // HELLO, WORLD!
用于删除字符串的头尾空白符,空白符包括:空格、制表符 tab、换行符等其他空白符等
不改变原字符串
let str = " JavaScript ";
console.log(str.trim()); // JavaScript
检测字符串是否以指定的子字符串开始
不改变原字符串
let str = "Hello, World!";
console.log(str.startsWith('hello')) // false
检测字符串是否以指定的子字符串开始
不改变原字符串
let str = "Hello, World!";
console.log(str.endsWith('World!')) // true
在字符串内检索指定的值,或找到一个或多个正则表达式的匹配
不改变原字符串
let str = "Hello, World!";
console.log(str.match(/l/)) // [ 'l', index: 2, input: 'Hello, World!', groups: undefined ]
console.log(str.match(/l/g)) // [ 'l', 'l', 'l' ]
console.log(str.match(/x/g)) // null
用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串
不改变原字符串
let str = "Hello, World!";
console.log(str.replace("Hello","JavaScript")) // JavaScript, World!
console.log(str.replace(/l/g,"$")) // He$$o, Wor$d!
用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串,该函数会替换所有匹配到的子字符串
使用正则表达式个,必须带上全局匹配标志g,否则会报错
不改变原字符串
let str = "Hello, World!";
console.log(str.replaceAll("l","&")) // He&&o, Wor&d!
console.log(str.replaceAll(/l/ig,"*")) // He**o, Wor*d!
将字符串复制指定次数
不改变原字符串
let str = "Hello, World!";
console.log(str.repeat(2)) // Hello, World!Hello, World!