HelloWorld与控制台
<body>
<!--Hello World!-->
<script type="text/javascript">
alert("Hello World!")
</script>
</body>
html的body或head标签里面写<script type="text/javascript"></script>
标签就能写JS语句,
alert是弹窗显示内容,运行时可以在浏览器右键审查元素,里面有控制台,一般在这里调试。
控制台可以用console.log(‘hello’)来打印结果。
数据类型
基本数据类型:
- Number - - (数字)
- String - - (字符串)
- Boolean - - (布尔值)
- Undefined - - (未定义)
- Null - - (空的)(其实是对象)
- Symbol - - (符号)
引用数据类型:
- Object - - (对象),以下都是属于Object之内的:
- Array - - (数组)
- Function - - (函数)
- Date - - (时间)
- RegExp - - (正则)
- …(还有很多)
定义一般用var,ES6可以用let,常量用const。
typeof NaN
'number'
typeof '123'
'string'
typeof true
'boolean'
typeof undefined
'undefined'
typeof null
'object'
typeof [1,2,3]
'object'
typeof Math.abs
'function'
typeof Math.abs()
'number'
String
console.log('a') //单引号双引号都行
console.log('\'a\'') //转义字符打出引号
console.log(`
多行
字符串
输出
`) //飘号``输出多行字符串
var name='小明'
console.log(`名字是:${name}`) //飘号输出模板字符串
var msg = 'Hello'
console.log(msg)
console.log(msg.length)
console.log(msg.toUpperCase())
console.log(msg.toLowerCase())
console.log(msg.indexOf('l')) //获取字符在字符串中的位置
console.log(msg.substring(2,4))
console.log(msg[0]) //可以通过字符串点调用方法,通过下标取字符串的元素
//注意字符串是不可变的,不能用下标赋值
数组
JS的数组可以包含任意数据类型。
var arr = [2,4,6,8]
console.log(arr)
console.log(arr.length)
arr[5] = 10 //数组自动变长
console.log(arr.length)
console.log(arr.indexOf(2))
console.log(arr.slice(1,5)) //截取数组输出新数组
console.log(arr.pop())
arr.push(1) //插入尾部
console.log(arr)
console.log(arr.shift())
arr.unshift(3) //插入头部
console.log(arr)
排序、拼接:
let arr = [5,3,7,9,1]
console.log(arr.sort())
console.log(arr.reverse())
console.log(arr.concat('A','B','C'))
console.log(arr.join('-')) //打印数组,用字符拼接
排序会改变数组本身,concat不改变。
对象
JS的对象相当于一组键值对。
键都是字符串,值是任意对象。
let person = {
name: 'rosemary',
age:23
}
console.log(person)
console.log(person.haha) //不存在的属性不会报错,只会undefined
delete person.name //可以动态地删除属性
console.log(person)
person.haha = 'haha' //添加属性
console.log(person['haha']) //下标方式取属性
分支与循环语句
与Java很像。if分支:
let age = 11;
if(age <= 3){
alert('cry!!')
}else if(age <= 10){
alert('haha!')
}else {
alert('sad')
}
循环:
- while
- do while
- for
- forEach
- for in
- for of
while循环:(do while 跟Java也是一样的)
while (age<100){
age++;
}
for循环:
for (let i = 0; i < 10; i++) {
console.log(i)
}
forEach循环:
let arr = [15,25,35]
arr.forEach(function (value) {
console.log(value)
})
for in循环:
for (let num in arr){
console.log(arr[num])
}
这里的num取到的是下标。
for of循环:
for (let num of arr){
console.log(num)
}
这里的num取到的是具体的值。
Map和Set集合
ES6的新特性。
Map:
let map = new Map([['tom',100],['jerry',90],['rose',80]]);
alert(map.get('rose')) //输出80,就是用键取值
for (let i of map){ //for of 遍历键与值
alert(i)
}
map.set('admin',70)
map.delete('tom')
console.log(map)
Set:无序且去重的集合
let set = new Set([3,1,1,1]) //自动去重,相当于一个1
console.log(set)
for (let i of set){ //for of 遍历键与值
console.log(i)
}
set.add(4) //添加值
console.log(set.has(4)) //判断set中是否包含某元素,返回布尔值
函数定义和参数获取
由于JS的弱类型特性,定义函数的时候不需要声明数据类型。
function abs(x){
if (x<0){
return -x;
}else {
return x;
}
}
alert(abs(-3))
返回就是直接return,不需要事先声明。
如果没有return,执行完也会返回结果,即undefined。
JS中的函数可以传送任意个参数,且不存在重载。
试执行alert(abs()),返回undefined,如何规避调用者不传参数呢:
function abs(x){
if(typeof x !== "number"){
throw 'not a number!'
}
if (x<0){
return -x;
}else {
return x;
}
}
alert(abs())
这样可以在控制台抛出not a number!
此外可以用arguments获取参数,arguments相当于一个实参的数组。
function print() {
for (let i = 0; i < arguments.length; i++) {
console.log(arguments[i])
}
}
print(1,2,3,5)
通过遍历arguments,打印出了传入的每一个参数。
ES6新特性:…rest
function print2(x,...rest) {
for (let i = 0; i < rest.length; i++) {
console.log(rest[i])
}
}
print2(1,2,3,5)
传入参数的时候,最后加一个参数叫做…rest,就可以将超出形参列表的参数作为数组放到rest中,
执行这段代码,只会打出2,3,5,因为1是形参列表中的x不在rest中。
变量的作用域
全局作用域:
- 全局作用域在页面打开时被创建,页面关闭时被销毁。
- 编写在script标签中的变量和函数,作用域为全局,在页面的任意位置都可以访问到。
- 在全局作用域中有全局对象window,代表一个浏览器窗口,由浏览器创建,可以直接调用。
- 全局作用域中声明的变量和函数会作为window对象的属性和方法保存。
- window对象的属性和方法可以直接调用,如window.an() 可以写为 an()。
函数作用域:
- 调用函数时,函数作用域被创建,函数执行完毕,函数作用域被销毁。
- 每调用一次函数就会创建一个新的函数作用域,他们之间是相互独立的。
- 在函数作用域中可以访问到全局作用域的变量,在函数外无法访问到函数作用域内的变量。
- 在函数作用域中访问变量、函数时,会先在自身作用域中寻找,若没有找到,则会到函数的上一级作用域中寻找,一直到全局作用域(类似于双亲委派机制)。
- 在函数作用域中也有声明提前的特性,对于变量和函数都起作用,此时函数作用域相当于一个小的全局作用域。
- 在函数作用域中,不使用变量关键字声明的变量,在赋值时会往上一级作用域寻找已经声明的同名变量,直到全局作用域时还没找到,则会成为window的属性。
- 在函数中定义形参,等同于声明变量。
an();
bn();
function an(){
var s = 'an'
console.log(s);
}
var bn = function() {
console.log('bn')
}
这段代码中,an作为函数被声明,可以把调用写在声明前面。而bn作为变量被声明,要先声明完,后面才能调用(运行会报bn不是函数的错误)。(即声明提前。赋值不提前)
闭包:为了在全局使用函数作用域中的变量。
养成规范:所有的变量定义放在script标签或函数的头部。
块级作用域let
for (var i = 0; i < 5; i++) {
console.log(i)
}
alert(i)
执行,alert出来的是5试将var改为let,则alert不出来并报i未定义,这就是块级作用域。
let关键字定义for后面的i时,旨在这次fori循环中有效,var则不然,所以推荐用let。
{
let a = 10;
}
alert(a)
执行,保存,a未定义,吧alert移进花括号内则可以打出,let改为var也可以打出,
这意味着,花括号是let的作用边界。
常量const
const也是ES6才有的,用来定义常量,以前没有这个关键字,常量就靠变量名大写,大家约定俗成不去修改。
const PI = 3.14;
PI = 2;
这样直接爆红。
方法的定义与调用,及apply
let Rosemary = {
name:'Rosemary',
birth:1998,
age: function () {
let date = new Date();
return date.getFullYear()-this.birth;
}
}
控制台可以查看一下age,算出来是24,明年就不同了。这里this指向的显然是Rosemary对象。
可以把方法写在外面:
function getAge() {
let date = new Date();
return date.getFullYear()-this.birth;
}
let Rosemary = {
name:'Rosemary',
birth:1998,
age: getAge
}
alert(Rosemary.age())
这样仍能得到24,不会报错(调用方法时加括号就报错了)。
如果在Rosemary之外调用,如alert(getAge()),则打不出来,因为this指向的是window,而window中没有birth属性。这时可以用apply改变this的指向:
alert(getAge.apply(Rosemary))
这样就是24了。
Date日期对象
let now = new Date()
now.getFullYear() //年
now.getMonth() //月(0~11)
now.getDate() //日
now.getDay() //星期几
now.getHours() //时
now.getMinutes() //分
now.getSeconds() //秒
now.getTime() //时间戳
console.log(new Date(1664334733061)) //根据时间戳返回当前的时间
console.log(now.toLocaleString()) //转换为当前本地时间
JSON对象
Json即:JavaScript Object Notation(JavaScript 对象表示法)
JSON 是存储和交换文本信息的语法,类似 XML。
JSON 比 XML 更小、更快,更易解析。
JSON 易于人阅读和编写。
C、Python、C++、Java、PHP、Go等编程语言都支持 JSON。
例如:
{
"sites": [
{ "name":"菜鸟教程" , "url":"www.runoob.com" },
{ "name":"google" , "url":"www.google.com" },
{ "name":"微博" , "url":"www.weibo.com" }
]
}
这个 sites 对象是包含 3 个站点记录(对象)的数组。
(还有个叫BSON的,是二进制的JSON,主要用在MongoDB)
打开搜索引擎——审查元素——网络——suggest?callback——响应,就能看到json:
在JavaScript中,一切皆为对象,任何JS支持的数据类型都可以用json来表示。
JSON 语法是 JavaScript 对象表示语法的子集:
- 数据在键值对中
- 数据由逗号 , 分隔
- 使用斜杆 \ 来转义字符
- 大括号 {} 保存对象
- 中括号 [] 保存数组,数组可以包含多个对象
JSON字符串 和 JS对象 的转化:
let user = {
name:'chan',
gender:'male',
age:'23'
}
let JasonUser = JSON.stringify(user)
console.log(user)
console.log(JasonUser)
打出user和JsonUser,发现user是JS里的对象,可以展开;
而JsonUser是字符串形式的对象。
再逆转化一下:
console.log(JSON.parse('{"name":"chan","gender":"male","age":"23"}'))
这样输出的就是对象了,和user无异。
原型继承与class继承
JS以前没有class继承,都是用原型来实现继承的:
let student = {
name:'学生',
study:function (){
console.log(this.name + 'study...')
}
}
let xiaoming = {
name:'小明'
}
在这里,小明不能使用学生的study方法,因为没有继承。
使用原型继承,只需要加一句声明:
xiaoming.__proto__ = student
小明就能study了。但很显然,这个原型是可以动态改变的,跟java的父类不一样。
为了好看一点,ES6引入了class继承。
class student{
constructor(name) {
this.name = name
}
study(){
console.log(this.name + 'study')
}
}
let xiaoming = new student('小明')
xiaoming.study()
这是定义了学生类,再实例化了一个小明,下面写一个小学生继承一下学生类:
class pupil extends student{
constructor(name,grade) {
super(name);
this.grade = grade
}
say(){
console.log('我叫' + this.name + ',今年' + this.grade + '年级了')
}
}
let xiaohua = new pupil('小花',3)
xiaohua.say()
console.log一下小花,发现这种继承仍然是通过原型实现的。
pupil的原型是student,student的原型是Object,Object的原型继续指向Object,这就是原型链。
Document对象模型(DOM)
BOM对象
BOM对象:Browser Object Model
DOM对象:Document Object Model
Browser就是浏览器,JavaScript的诞生就是为了在浏览器中运行。
浏览器(内核):
- IE6~11
- Chrome
- safari(苹果)
- FireFox(Linux)
第三方(内核是可选的):
- QQ浏览器
- 360浏览器
window对象代表浏览器窗口。
例如alert方法,本质是window.alert,是在window中的。
console.log(location)
location指向当前页面的url信息。
//主机
host: "localhost:63342"
//当前指向位置
href: "http://localhost:63342/webdemo/JS2/Demo18.html?_ijt=uf3auigeo19mgc2slo3jmhpup0"
//设置新的地址
assign('https://www.baidu.com/')
history代表浏览器的历史记录,可以点back后退,点forward前进。
获得DOM节点
对DOM的操作主要有:
- 获得DOM节点
- 更新DOM节点
- 删除DOM节点
- 增加DOM节点
html页面相当于一个DOM树形结构。几种常用的获得DOM节点的方法:
- getElementsByTagName()
- getElementById()
- getElementsByClassName()
- getElementsByName()
<div id="father">
<h1>标题1</h1>
<p id="p1">p1</p>
<p class="p2">p2</p>
</div>
<script type="text/javascript">
let h1 = document.getElementsByTagName('h1')
let p1 = document.getElementById('p1')
let p2 = document.getElementsByClassName('p2')
let father = document.getElementById('father')
let children = father.children //获取父节点下所有的子节点
// father.firstChild
// father.lastChild
console.log(h1)
console.log(p1)
console.log(p2)
console.log(father)
console.log(children)
</script>
这是原生代码,之后可以使用jQuery。
更新DOM节点
<div id="id1"></div>
<script type="text/javascript">
let id1 = document.getElementById('id1')
id1.innerText = '123' //修改文本
id1.innerHTML = '<strong>123</strong>' //超文本,可以加html标签
//修改CSS样式
id1.style.color = 'red' //修改颜色
id1.style.fontSize = '100px' //字体大小
</script>
实操一下,打开百度首页的控制台,执行两条指令:
let ss = document.getElementById('su')
undefined
ss.style.backgroundColor = 'violet'
"violet"
紫色百度就出来了:
注意更新节点的属性全部用引号包裹。
删除DOM节点
删除节点的逻辑:获得节点的父节点,通过父节点删除。
<div id="father">
<p id="p1">长子</p>
<p id="p2">次子</p>
<p id="p3">幼子</p>
</div>
<script type="text/javascript">
let self = document.getElementById('p1')
let father = document.getElementById('father')
// self.remove()
father.removeChild(self) //删除指定子节点
father.children[0].remove(); //删除下标为0的子节点
</script>
获得父节点的方法:parentElement
删除子节点的方法:removeChild
删除自己的方法:remove。这样好像不用父节点也能删啊。。。
上面代码运行后就只会打出幼子,长子和次子已被删除。
创建与插入DOM节点
如果我们获得了一个空节点,innerHTML就可以设置节点了,但如果这个节点不是空的,
原来的内容就会被覆盖。
本节目录:
- 移动节点
- 创建节点
- 创建script标签节点
- 获取body标签节点
- 插入节点
移动节点:
<p id="js">JavaScript</p>
<div id="list">
<p id="se">JavaSE</p>
<p id="ee">JavaEE</p>
</div>
<script type="text/javascript">
let js = document.getElementById('js')
let list = document.getElementById('list')
list.appendChild(js)
</script>
通过appendChild方法,js被移到了list中,原来位置的js就没了。
创建节点:
<div id="div"></div>
<script type="text/javascript">
let newP = document.createElement('p')
newP.id = 'newP'
newP.innerText = '新标签'
let list = document.getElementById('div')
list.appendChild(newP)
</script>
通过createElement方法,创建一个新的节点。
创建script标签节点:
let myScript = document.createElement('script')
myScript.setAttribute('type','text/javascript')
myScript.innerText = 'alert(\'套娃\')'
list.appendChild(myScript)
相当于我们添加了一个script标签,并通过这个标签的innerText打出了‘套娃’。
打开浏览器控制台,打出list,看到我们创建在里面的两个标签:
<div id="div">
<p id="newP">新标签</p>
<script type="text/javascript">alert('套娃')</script>
</div>
注意setAttribute是万能方法,参数是键值对,同样可以用来设置id之类的。
获取body标签节点:
let body = document.getElementsByTagName('body')
body[0].style.backgroundColor = 'green'
可见body同样是可以进行DOM操作的。
这里需要注意,getElements的方法不同于getElement的方法,返回的是数组,所有body要加下标[0]。
可以获取body标签,就可以在body下面直接appendChild了。
插入节点:
<div id="list">
<p id="A">A</p>
<p id="B">B</p>
<p id="C">C</p>
<p id="D">D</p>
</div>
<script type="text/javascript">
let aa = document.getElementById('A')
let dd = document.getElementById('D')
let list = document.getElementById('list')
list.insertBefore(aa,dd)
</script>
本来四个p标签是ABCD排好序的,通过insertBefore方法,将A插到了D的前面,
执行以上代码,打出来的顺序是BCAD。
DOM表单操作
表单常见的节点:
- 文本框
- 下拉框
- 单选框
- 多选框
- 隐藏域
- 密码框
- 等等。。。
表单的目的:提交信息。
简单的示例:
<form action="post">
<p>
<span>用户名:</span><input type="text" id="username">
</p>
<p>
<span>性别:</span>
<input type="radio" name="gender" value="male">男
<input type="radio" name="gender" value="female">女
</p>
</form>
<script type="text/javascript">
let username = document.getElementById('username')
let male = document.getElementsByName('gender')[0]
let female = document.getElementsByName('gender')[1]
</script>
可以用male.checked查看是否选中了性别男。