JavaScript
Goal
● Link js file with html file
● Variables
● Data Types
● Functions
● IIFE
● Callback Function
non-goal
● Higher-order function
eve@Eves-Air JavaScript % node -v
v19.3.0
eve@Eves-Air JavaScript % ls
JavaScript.assets JavaScript.md jstest.js
eve@Eves-Air JavaScript % node jstest.js // 这里的node也相当于javascript的运行环境 可以使其当后端使用
1
eve@Eves-Air JavaScript %
js在终端下可以用node执行命令, 而若没有node则需要将其接入html语言中, 让浏览器来执行
1. Link js file with html file
2 methods:
-
method 1: inline mode
-
method 2: using
<script>
tag - internal mode
- external mode
Method1: inline mode - not recommend
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1 onclick="alert('hello')">JavaScript</h1>
<!-- onclick表示点击触发h1; alert()表示javascript build in的一个函数 -->
</body>
</html>
不利于代码修改和维护, 所以不推荐
Method2: using <script>
tag
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!--method 1-->
<!--using script tag -internal style-->
<script type = "text/javascript">
console.log('haha');
</script>
<!--method 2-->
<!-- using script tag - external style -->
<script type = "text/javascript" src="jstest.js"></script>
</head>
<body>
<!--inline mode-->
<h1 onclick="alert('hello')">JavaScript</h1>
<!-- onclick表示点击触发; alert()表示javascript build in的一个函数 -->
<!--inline mode-->
<h1 onclick="alert('hello from the body section')">I am headline</h1>
<!--using script tag -internal style-->
<script type = "text/javascript">
alert("hello from script tag");
</script>
<!-- using script tag - external style -->
<script type = "text/javascript" src="main.js"></script>
</body>
</html>
如何判断一个语言是否为编程语言?
- 变量
- data structure
- 函数(方法)
- 运算 逻辑
html, css不是编程语言
2. Variables
- how to decare:
- var 来声明变量
- let
- const
Javascript是弱类型变量.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script type = "text/javascript">
var a = 10; // step 1: var a; step 2: a = 10;
a = "haha"; // 类型是由赋值的时候决定的, 这种就叫做弱类型变量
console.log(a);
var b;
console.log(b); // 打印出来是个undefine
</script>
</body>
</html>
3. Data Types
-
JavaScript is “dynamically typed
-
there are data types, but variables are not bound to any of them.
-
two types of data
-
primitive
- Number, String, Boolean
- reference value
- Object, Array, Function
-
two special values
- undefined 表示未赋值
-
null
-
==
/===
==
the same value, but types may be different===
the same value, the same type
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script type = "text/javascript">
var arr = [1, 2, 3];
var arr2 = [1, "haha", true, ['a','b','c'], function(){}];
var obj = {
name: "Snoopy",
age: 10
}
var obj2 = {
haha:obj
}
</script>
<h1> JavaScript </h1>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script type = "text/javascript">
var name = "tiger";
var obj3 = {
[name]: "hello"
}
var obj4 = {
tiger: "hello"
}
// obj4[name]: -> name -> value -> obj4.key
// obj.name -> obj4.key
</script>
<h1> JavaScript </h1>
</body>
</html>
The sample codes:
// The primitive
// Number
var a = 1;
var b = 3.14;
// String
var firstName = "Joe";
var lastName = "Curry";
// Boolean
var isGood = true;
var isTrue = false;
// The reference
// Array
var arr = [1, 2, 3]
var arr1 = [1, 2, 'Joe', 'Curry', 3, undefined, function(){}]
// Object
var person = {
//key : value
name : "Joe Curry",
age : 20,
job : "Full Stack Engineer",
// a method in an object
sayHello : function(){
console.log("hello, this is Joe!");
}
}
console.log(person.name);
console.log(person['age']);
person.age = 21;
person['age'] = 30;
person.sayHello();
// Two special values
// undefined - special
value in js
var test;
console.log(test);
// null
var empty = null;
undefined表示未赋值, 而红字则说明d未定义.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number
4. Function
Javascript实际上是个函数式编程的思想
3 ways to define a function
- The function declaration
- The function expression
- Arrow function
// The method 1: function declaration
// Declare the function 'myFunc'
function myFunc(paras){
console.log('this is my function');
console.log(paras + 1);
}
// The method 2: function expression
// define a function named 'myFunc'
var myFunc = function (paras){
console.log('this is my function');
console.log(paras + 1);
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
var x = 10;
if (x > 2){
for (var i = 0; i < 3; i++){
console.log(i);
}
}
if (x < 20){
for (var i = 0; i < 3; i++){
console.log(i);
}
}
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
function f() {
for(var i = 0; i < 3; i++){
console.log(i);
}
}
var x = 10;
if (x > 2){
// for (var i = 0; i < 3; i++){
// console.log(i);
// }
f();
}
if (x < 20){
// for (var i = 0; i < 3; i++){
// console.log(i);
// }
f();
}
</script>
</body>
</html>
5. IIFE
● Immediately Invoked Function Expression
● A JavaScript function that runs as soon as it is defined.
var result = (function(){
var name = "Battry";
return name;
})();
// Immediately creates the output: result;
// "Battry"
// Immediately print "this is IIFE":
(function (){
console.log('this is IIFE')
})();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
// IIFE -> 有两个括号
(function(){
console.log("IIFE");
})();
// 第一个括号里是函数的定义,最后的是调用
</script>
</body>
</html>
test.js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type = "text/javascript" src = "test.js"></script>
<!-- 通过js 标签引入的 var 变量挂在window 下, 作为全局变量-->
<!-- <script type = "text/javascript" src = "jsdemo.js"></script>-->
</head>
<body>
<script type = "text/javascript">
// 我能不能访问 test.js 中的var
console.log(window.obj)
</script>
</body>
</html>
jsdemo.js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type = "text/javascript" src = "test.js"></script>
<!-- 通过js 标签引入的 var 变量挂在window 下, 作为全局变量-->
<script type = "text/javascript" src = "jsdemo.js"></script>
<!-- 如果两个 都引入 那么它会打印哪儿个 -->
</head>
<body>
<script type = "text/javascript">
// 我能不能访问 test.js 中的var
console.log(window.obj);
</script>
</body>
</html>
所以引入顺序很重要, 不然会被替换掉.
jsdemo.js
写成这样
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type = "text/javascript" src = "jsdemo.js"></script>
</head>
<body>
<script type = "text/javascript">
// 我能不能访问 test.js 中的var
console.log(window.obj);
</script>
</body>
</html>
写入function中, fucntion立刻被执行, obj被gc掉了
jsdemo.js
(function(){ // 立即执行函数
var obj = {
name: "haha",
age: 1
}
window.jstest = { // 可以利用window向它暴露任何你想暴露的 变成全局变量
obj: obj
}
})();
// js中插件的写法都是这么来的
test.js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type = "text/javascript" src = "jsdemo.js"></script>
<script type = "text/javascript" src = "test.js"></script>
</head>
<body>
<script type = "text/javascript">
console.log(window.jstest.obj); // 拿到了jstest下的obj
console.log(window.test.obj); // test下的也可以拿到
</script>
</body>
</html>
这种写法, 两个就都可以拿到. 这种开发中模块化写法经常用到
6. Callback Function
回调函数, 不是一个特殊的函数, 实际上它是一个特殊的设计模型. swift语言中也有回调函数的设计理念, 虽然它不叫回调函数, 但其设计理念类似.
A callback function is a function passed into another function as an argument, which is then invoked inside the outer function to complete some kind of routine or action.
function guess (num, smallerCb, biggerCb, equalCb){
var random = Math.floor(Math.random() * 100);
console.log(num, random);
if (num < random){
smallerCb();
return;
}else if (num > random){
biggerCb();
return;
}
equalCb();
}
fucntion smaller(){
console.log('Your num is too small');
}
function bigger(){
console.log('Your num is too bigger');
}
function equal(){
console.log('Your guess is right!');
}
guess(18, smaller, bigger, equal);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script type = "text/javascript">
// Callback fn
function test(cb){
console.log(cb); // print cb
}
test([1, 2, 3]);
test({a:1, b:2});
var haha = [1, 2, function(){}];
test(haha);
var hehe = function(){
console.log('this is hehe');
}
test(hehe);
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script type = "text/javascript">
// Callback fn
function test(cb){
console.log(cb); // print cb
cb(); // 此处cb输入为函数 可以执行下
}
var hehe = function(){
console.log('this is hehe');
}
test(hehe);
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script type = "text/javascript">
// Callback fn
function test(cb){
console.log(cb); // print cb
cb(); // 此处cb输入为函数 可以执行下 如果不是函数或者不传参 就是undefined
}
var hehe = function(){
console.log('this is hehe');
}
test(hehe);
</script>
</body>
</html>
此种写法, 是函数才能执行, 不是函数不能执行.
回调函数总结:
- 它本身是函数
- 它的参数是函数 parameters is function
回调函数的好处
arr下面有个方法 叫做forEach, 它里头必须要跟一个函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
var arr = [1, 2, 3];
arr.forEach(function(item){ // forEach是array自带的一个函数 是js给你的
console.log(item); // forEach里只接受回调函数
});
function forEach(cb){ // forEach大概就是这样子
for(var i = 0; i < arr.length; i++){
cb(i);
}
}
</script>
</body>
</html>
Why use callback?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
// Why use callback?
function cal(a, b, type){
if(type == "+"){
return a + b;
} else if (type == "-"){
return a - b;
} else{
return a + ", " + b;
}
}
console.log(cal(1, 2, "+"));
console.log(cal(1, 2));
// 可扩展性差, 目前可以检查的类型只有+, -, ,
// 如果以后还要再增加功能, 则要touch源码.
function newCal(a, b, cb){ // 相当于供货商
return cb(a, b);
}
//相当于用户
function add(a, b){
return a + b;
}
function mul(a, b){
return a*b;
}
console.log(newCal(3, 6, mul));
</script>
</body>
</html>
加入断点查看刷新页面查看
回调函数很复杂, 它有同步的和异步的. 回调函数也会有弊端, 它会形成多重回调, 它就会形成回调黑洞. 而解决方式则为promise. 它比回调函数更高一级.
7. JavaScript Advanced
Browser Environment
Two roles of the root object - window:
● A global object for JavaScript code
● A browser window and APIs.
// global functions are methods of the global object
var a = 10;
function sayHi(){
alert("Hello");
}
window.sayHi();
console.log(window.a);
a 存在window下了. 此时它的角色是js的全局变量.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>DOM + Event</h1>
<script>
var a = 10;
function sayHi(){
alert("Hello");
}
window.sayHi();
console.log(window.a);
console.log(window.innerHeight); // 隶属于window的属性
console.log(window); // 是browser 提供的api方法, 隶属于BOM下
</script>
</body>
</html>…
在console中输入window, 然后就会显示自己的window, 并且它显示了react. 而查看自己先前写的window下面是没有的.
在window中查看大小
调整大小后, 查看窗口大小的变化.
所以看到window所代表的大小是白色的窗口大小. 这个时候提供的就是BOM, 作为窗口来使用的.
而这个window则是JavaScript下的全局变脸来使用的.
所以要注意两者的角色不同. 其实就是用了window一个概念, 来表示不同的.
8.DOM and APIs
What is DOM
● Document Object Model that represents all page content as objects that can be modified.
● A document object is the main entry to the page.
DOM就是window下面的一个对象. 如下图, 当输入document的时候, 窗口的可视区域变蓝了.
What is DOM Tree
The DOM represents HTML as a tree structure of tags.
<html>
<head>
<title>My Document</title>
</head>
<body>
<h1>
Header
</h1>
<p>
Paragraph
</p>
</body>
</html>
对页面的操作, 实际上就是对DOM的操作.
how to access dom tree
- document.getElementById(id value)
The Document method getElementById() returns an Element object representing the element whose id property matches the specified string. Since element IDs are required to be unique if specified, they're a useful way to get access to a specific element quickly.
- (elem/document).getElementsByClassName(className)
The Element method getElementsByClassName() returns a live HTMLCollection which contains every descendant element which has the specified class name or names.
The method getElementsByClassName() on the Document interface works essentially the same way, except it acts on the entire document, starting at the document root.
- (elem/document).getElementsByTagName(tag name)
The Element.getElementsByTagName() method returns a live HTMLCollection of elements with the given tag name. All descendants of the specified element are searched, but not the element itself.
Element.getElementsByTagName is similar to Document.getElementsByTagName(), except that it only searches for elements that are descendants of the specified element.
- (elem/document).querySelector(css selector)
The Document method querySelector() returns the first Element within the document that matches the specified selector, or group of selectors. If no matches are found, null is returned.
- (elem/document).querySelectorAll(css selector)
The Document method querySelectorAll() returns a static (not live) NodeList representing a list of the document's elements that match the specified group of selectors.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div class ="user common">User: </div>
<ul class = "box common">
<li id = "john">John</li>
<li id = "peter">Peter</li>
</ul>
</body>
<script>
</script>
</html>
现在要拿John的属性
document一回车拿倒是整个document的页面.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div class ="user common">User: </div>
<ul class = "box common">
<li id = "john">John</li>
<li id = "peter">Peter</li>
</ul>
</body>
<script>
// copy codes on the right
var oUser = document.getElementById('user');
console.log(oUser);
var oCommon = document.getElementsByClassName('common');
console.log(oCommon);
var oUl = document.getElementsByClassName('box');
var oLis = document.getElementsByTagName('li');
var join = oUl[0].getElementsByTagName('li')[0]; // 这样更有效了, 缩小了搜索范围 ID不行 它是唯一的, 全局的
console.log(oUl);
console.log(oLis);
console.log(join);
//querySelector
var oUser2 = document.querySelector('#user'); // #是ID
console.log(oUser2);
var oLis2 = document.querySelectorAll('li');
console.log(oLis2);
var oCommons = document.querySelectorAll('.common');// 因为有两个common, all了两个都找到了
console.log(oCommons);
</script>
</html>
how to create elements
- (document.createElement(tag)
creates an element with the given tag
- elem.innerHTML
add content to the given element
- elem.append(newElem) (old school: elem.appendChild(newElem))
insert into elem, at the end
- elem.prepend(newElem)
insert into elem, at the beginning
- elem.before(newElem)
insert right before elem
- elem.after(newElem)
insert right after elem
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<ol id="ol">
<li>0</li>
<li>1</li>
<li>2</li>
</ol>
<script>
var ol = document.getElementById('ol');
var newLi1 = document.createElement('li'); //创建li节点
newLi1.innerHTML = 'append'; // 在创建的li中,加 'append' 这个字符串
ol.append(newLi1); // 要通过id 挂上去
var newLi2 = document.createElement('li');
newLi2.innerHTML = '<strong>prepend</strong>'; // 要加'prepend' 这个字符串
//newLi2.innerText = '<strong>prepend</strong>'; // 则会把标签当作字符串.
ol.prepend(newLi2);
ol.before('before'); // 创建了一个新的指数节点, 和ol是同辈, 同级别的
ol.after('after');
// 一般不会这么写 就是要知道可以这么写
</script>
</body>
</html>
graph TD;
A --> B[before]
A --> ol[ol]
A --> C[after]
A --> D[...]
ol --> ol1[append]
ol --> ol2[prepend]
how to create/update elem’s attribute
- elem.className = 'class name'
A string value, add a class to a given element
- elem.classList.add/remove(‘class name’)
add/remove multiple class
- elem.style.cssText
assign element style properties
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
this is a div
</div>
<p> this is a paragraph </p>
<script>
var oDiv = document.getElementsByTagName('div')[0]; // 0 代表document中的第一个div
oDiv.className = 'box'; // a string value, add a class to a given element
// oDiv.className = 'box1 box2'; //要加两个类也可以这么加,注意空格, 会覆盖掉之前的
oDiv.className += " box2"; // 添加第二个类, 但是要注意前面要空格
// 第二种写法:
var oP = document.getElementsByTagName('p')[0];
oP.classList.add('box'); // add/remove multiple class
oP.classList.add('common'); // 此处是给 p 加了两个类
oDiv.style.color = 'red'; //assign element style properties
</script>
</body>
</html>
9. Event
what is an event?
- An event is a signal that something has happened
some useful dom events
- click – when the mouse clicks on an element
- mouseover – when the mouse cursor comes over
- mousemove – when the mouse is moved
event handler
- a function that runs in case of an event
#### Add event listener
ways to add event listener
- on html - onEventName
- elem.onEventName
- elem.addEventListener
More about addEventListener:
element.addEventListener(event, function, useCapture)
useCapture - Optional. A Boolean value that specifies whether the event should be executed in the capturing or in the bubbling phase.
Possible values:
● true - The event handler is executed in the capturing phase
● false- Default. The event handler is executed in the bubbling phase
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
div{
background-color:lightblue;
width: 200px;
height: 200px;
}
</style>
</head>
<body>
<div></div>
<!--Method 1: add event handler on html-->
<h2 onclick="alert('hello')">Hello Event</h2>
<!-- 不推荐使用这种方式, 因为它 与html javascript 没有很好的区分-->
<!--Method 2: elem.click-->
<script>
var oDiv = document.getElementsByTagName('div')[0];
oDiv.onclick = function(){
oDiv.style.background = 'red';
}
</script>
<!--onclick是属性值,如果出现多次onclick, 它以最后的为准,它会把之前的给覆盖掉 -->
<!--Method 3: elem.addEventListener-->
<script>
var oDiv = document.getElementsByTagName('div')[0];
oDiv.addEventListener('click', function(){
console.log('clicked');
},false);
// element.addEventListener(event, function, useCapture)
// 1.event 事件
// 2.function 你要对event做些什么
// 3.true or false
// 每次点击模块, 在console中就增加次数
// 一般在开发中使用addEventListener. 因为可以控制第三个属性
console.log(1)
oDiv.addEventListener('click', function(evt){
console.log('clicked2'); // 这是个回调函数,不是一上来就执行的,需要点击进行触发才执行
console.log(evt); // evt 是pointerEvent
},false); // 这是由js事件处理机制来决定的
console.log(2)
// 这样实际上是绑定的两个不同的函数,而不会像是onclick那样给覆盖掉
</script>
</body>
</html>
Event object
what is an event object
- the browser creates an event object, when an event happens
- The browser puts details into the event object and passes it as an argument to the handler.
what can we get from event object
- event target
- event type
- ...
### Bubbling and capturing
what is bubbling and capturing
冒泡和捕捉
Here’s the picture of a click on
The standard DOM Events describes 3 phases of event propagation:
- Capturing phase – the event goes down to the element.
- Target phase – the event reached the target element.
- Bubbling phase – the event bubbles up from the element.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Event 2</title>
<style>
div{
border: 1px solid black;
}
#grandparent{
height: 400px;
width: 400px;
background-color: lightgray;
}
#parent {
height: 200px;
width: 200px;
background-color: lightpink;
}
#child {
height: 100px;
width: 100px;
background-color: lightblue;
}
</style>
</head>
<body>
<div id="grandparent">
<div id="parent">
<div id="child"></div>
</div>
</div>
<!--1. Bubbling-->
<script>
var gDiv = document.getElementById('grandparent');
var pDiv = document.getElementById('parent');
var cDiv = document.getElementById('child');
gDiv.addEventListener('click', function(){
console.log('grandparent bubbling!');
},false);
pDiv.addEventListener('click', function(){
console.log("parent bubbling!");
},false);
cDiv.addEventListener('click', function() {
console.log("child bubbling!");
},false);
// ===== onclick has only bubbling ===
gDiv.onclick = function (){
console.log('gp')
};
pDiv.onclick = function(){
console.log('p')
};
cDiv.onclick = function(){
console.log('c')
};
// // ======== 2. capturing =====
// gDiv.addEventListener('click', function(){
// console.log("grandparent!");
// },true);
//
// cDiv.addEventListener('click', function(){
// console.log('child!');
// },true);
</script>
</body>
</html>
child-parent-grandparent 走的是绿的路线
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Event 2</title>
<style>
div{
border: 1px solid black;
}
#grandparent{
height: 400px;
width: 400px;
background-color: lightgray;
}
#parent {
height: 200px;
width: 200px;
background-color: lightpink;
}
#child {
height: 100px;
width: 100px;
background-color: lightblue;
}
</style>
</head>
<body>
<div id="grandparent">
<div id="parent">
<div id="child"></div>
</div>
</div>
<!--1. Bubbling-->
<script>
var gDiv = document.getElementById('grandparent');
var pDiv = document.getElementById('parent');
var cDiv = document.getElementById('child');
// gDiv.addEventListener('click', function(){
// console.log('grandparent bubbling!');
// },false);
//
// pDiv.addEventListener('click', function(){
// console.log("parent bubbling!");
// },false);
//
// cDiv.addEventListener('click', function() {
// console.log("child bubbling!");
// },false);
// ===== onclick has only bubbling ===
// gDiv.onclick = function (){
// console.log('gp')
// };
//
// pDiv.onclick = function(){
// console.log('p')
// };
//
// cDiv.onclick = function(){
// console.log('c')
// };
// ======== 2. capturing =====
gDiv.addEventListener('click', function(){
console.log("grandparent!");
},true);
pDiv.addEventListener('click', function(){
console.log('parent');
},true);
cDiv.addEventListener('click', function() {
console.log('child!');
},true);
</script>
</body>
</html>
grandparent-parent-child 走的是红色的路线
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Event 2</title>
<style>
div{
border: 1px solid black;
}
#grandparent{
height: 400px;
width: 400px;
background-color: lightgray;
}
#parent {
height: 200px;
width: 200px;
background-color: lightpink;
}
#child {
height: 100px;
width: 100px;
background-color: lightblue;
}
</style>
</head>
<body>
<div id="grandparent">
<div id="parent">
<div id="child"></div>
</div>
</div>
<!--1. Bubbling-->
<script>
var gDiv = document.getElementById('grandparent');
var pDiv = document.getElementById('parent');
var cDiv = document.getElementById('child');
// gDiv.addEventListener('click', function(){
// console.log('grandparent bubbling!');
// },false);
//
// pDiv.addEventListener('click', function(){
// console.log("parent bubbling!");
// },false);
//
// cDiv.addEventListener('click', function() {
// console.log("child bubbling!");
// },false);
// ===== onclick has only bubbling ===
gDiv.onclick = function (){
console.log('grandparent')
};
pDiv.onclick = function(){
console.log('parent')
};
cDiv.onclick = function(){
console.log('child')
};
// ======== 2. capturing =====
// gDiv.addEventListener('click', function(){
// console.log("grandparent!");
// },true);
//
// pDiv.addEventListener('click', function(){
// console.log('parent');
// },true);
//
// cDiv.addEventListener('click', function() {
// console.log('child!');
// },true);
</script>
</body>
</html>
onclick: child-parent-grandparent走的是绿的路线, bubbling.
如果同时绑定bubbling 和 capturing
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Event 2</title>
<style>
div{
border: 1px solid black;
}
#grandparent{
height: 400px;
width: 400px;
background-color: lightgray;
}
#parent {
height: 200px;
width: 200px;
background-color: lightpink;
}
#child {
height: 100px;
width: 100px;
background-color: lightblue;
}
</style>
</head>
<body>
<div id="grandparent">
<div id="parent">
<div id="child"></div>
</div>
</div>
<!--1. Bubbling-->
<script>
var gDiv = document.getElementById('grandparent');
var pDiv = document.getElementById('parent');
var cDiv = document.getElementById('child');
gDiv.addEventListener('click', function(){
console.log('grandparent bubbling!');
},false);
pDiv.addEventListener('click', function(){
console.log("parent bubbling!");
},false);
cDiv.addEventListener('click', function() {
console.log("child bubbling!");
},false);
// ===== onclick has only bubbling ===
// gDiv.onclick = function (){
// console.log('grandparent')
// };
//
// pDiv.onclick = function(){
// console.log('parent')
// };
//
// cDiv.onclick = function(){
// console.log('child')
// };
//
// ======== 2. capturing =====
gDiv.addEventListener('click', function(){
console.log("grandparent!");
},true);
pDiv.addEventListener('click', function(){
console.log('parent');
},true);
cDiv.addEventListener('click', function() {
console.log('child!');
},true);
</script>
</body>
</html>
路线是先红色后绿色,即使调换顺序也是这样
常见的应用在于事件代理.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<ul>
<li>apple</li>
<li>pear</li>
<li>cherry</li>
<li>blue berry</li> <!--比如此时我要再加个, 动态加个,而for已经固定了 不好-->
<!-- 就可以用bubbling来解决-->
</ul>
<!--现在有一个需求,我点击li 要获得里面的内容-->
<script>
// var olis = document.getElementsByTagName("li");
// for (var i = 0; i < olis.length; i++){
// olis[i].addEventListener('click', function(evt){
// console.log(evt.target.innerText);
// }, false);
// }
var oUl = document.getElementsByTagName("ul")[0]; //tag拿到的是collection
oUl.addEventListener("click", function(evt){
console.log(evt.target.innerText);
}, false)
// 这样的好处是我不在乎你要加多少个,我需要找到触发元就好了
</script>
</body>
</html>
how to stop bubbling / capturing
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
div {
border: 1px solid black;
}
.wrapper{
width: 200px;
height: 200px;
background-color: lightgray;
position: relative;
}
.apply {
width: 80px;
height: 30px;
background-color: antiquewhite;
line-height: 30px;
text-align: center;
position: absolute;
bottom: 20px;
right: 10px;
color:black;
}
</style>
</head>
<body>
<div class = "wrapper">
<div class="apply">Apply</div>
</div>
<script>
var wrapper = document.getElementsByClassName('wrapper')[0];
var apply = document.getElementsByClassName('apply')[0];
wrapper.addEventListener('click', function(e){
console.log('...wrapper...');
},false);
apply.addEventListener('click', function (event){
event.stopPropagation(); // without this every time click apply will print wrapper and apply
console.log('...apply...');
},false);
</script>
</body>
</html>
stop: just print what you click.
More Reading: https://medium.com/better-programming/how-javascript-works-1706b9b66c4d
JavaScript语言
也称为js,是我们整个前端基础的重点内容,只有了解了JavaScript语言,我们才能了解前端如何与后端交互。
JavaScript与Java没有毛关系,仅仅只是名字中包含了Java而已,跟Java比起来,它更像Python,它是一门解释型语言,不需要进行编译,它甚至可以直接在浏览器的命令窗口中运行。
它相当于是前端静态页面的一个补充,它可以让一个普通的页面在后台执行一些程序,比如我们点击一个按钮,我们可能希望执行某些操作,比如下载文件、页面跳转、页面弹窗、进行登陆等,都可以使用JavaScript来帮助我们实现。
我们来看看一个简单的JavaScript程序:
const arr = [0, 2, 1, 5, 9, 3, 4, 6, 7, 8]
for (let i = 0; i < arr.length; i++) {
for (let j = 0; j < arr.length - 1; j++) {
if(arr[j] > arr[j+1]){
const tmp = arr[j]
arr[j] = arr[j+1]
arr[j+1] = tmp
}
}
}
window.alert(arr)
这段代码实际上就是实现了一个冒泡排序算法,我们可以直接在页面的头部中引用此js文件,浏览器会在加载时自动执行js文件中编写的内容:
我们发现JS的语法和Java非常相似,但是它还是和Java存在一些不同之处,而且存在很多阴间语法,那么我们来看看JS的语法。
JavaScript基本语法
在js中,定义变量和Java中有一些不同,定义一个变量可以使用let
关键字或是var
关键字,IDEA推荐我们使用let
关键字,因为var
存在一定的设计缺陷一个变量可以被重复定义(这里就不做讲解了,之后一律使用let关键字进行变量声明):
上面的结果中,我们得到了a的结果是11,也就是说自增和自减运算在JS中也是支持的,并且JS每一句结尾可以不用加分号。
js并不是Java那样的强类型语言(任意变量的类型一定是明确的),它是一门弱类型语言,变量的类型并不会在一开始确定,因此我们在定义变量时无需指定变量的确切类型,而是在运行时动态解析类型:
我们发现,变量a已经被赋值为数字类型,但是我们依然在后续能将其赋值一个字符串,它的类型是随时可变的。
很多人说,这种变态的类型机制是JS的一大缺陷。
世界上只有两种语言:一种是很多人骂的,一种是没人用的。
我们接着来看看,JS中存在的基本数据类型:
- Number:数字类型(包括小数和整数)
- String:字符串类型(可以使用单引号或是双引号)
- Boolean:布尔类型(与Java一致)
还包括一些特殊值:
-
undefined:未定义 - 变量声明但不赋值默认为undefined
-
null:空值 - 等同于Java中的null
-
NaN:非数字 - 值不是合法数字,比如:
我们可以使用typeof
关键字来查看当前变量值的类型:
JavaScript逻辑运算和流程控制
我们接着来看看js中的关系运算符,包括如下8个关系运算符:大于(>),小于(<),小于等于(<=),大于等于(>=),相等(),不等(!=),全等(=),不全等(!==)
其实关系运算符大致和Java中的使用方法一致,不过它还可以进行字符串比较,有点像C++的语法:
那么,相等和全等有什么区别呢?
我们发现,在Java中,若运算符两边是不同的基本数据类型,会直接得到false,而JS中却不像这样,我们发现字符串的10居然等于数字10,而使用全等判断才是我们希望的结果。
==
的比较规则是:当操作数类型一样时,比较的规则和恒等运算符一样,都相等才相等,如果两个操作数是字符串,则进行字符串的比较,如果里面有一个操作数不是字符串,那两个操作数通过Number()方法进行转换,转成数字进行比较。
因此,我们上面进行的判断实际上是运算符两边都进行了数字转换的结果进行比较,自然也就得到了true,而全等判断才是我们在Java中认识的相等判断。
我们接着来看逻辑运算,JS中包括&&、||、&、|、?:等,我们先来看看位运算符:
实际上和Java中是一样的,那么我再来看看逻辑运算:
对于boolean变量的判断,是与Java一致的,但是JS也可以使用非Boolen类型变量进行判断:
和C/C++语言一样,0代表false,非0代表true,那么字符串呢?
我们发现,空串为false,非空串为true,我们再来看看:
我们发现,前者得到的结果为true,而后者得到的结果却是是7,真是滑天下之大稽,什么鬼玩意,实际上是因为,默认非0都是true,而后者又是先判断的7,因此会直接得到7而不是被转换为true
那么我们再来看看几个特殊值默认代表什么:
最后来使用一下三元运算符,实际上和Java中是一样的:
得益于JS的动态类型,emmm,三元运算符不一定需要固定的返回值类型。
JS的分支结构,实际上和Java是一样的,也是使用if-else语句来进行:
同理,多分支语句也能实现:
当然,多分支语句也可以使用switch来完成:
let a = "a"
switch (a){
case "a":
console.info("1")
break
case "b":
console.info("2")
break
case "c":
console.info("3")
break
default:
console.info("4")
}
接着我们来看看循环结构,其实循环结构也和Java相差不大:
mac中的换行: shift
+return
JavaScript函数定义
JS中的方法和Java中的方法定义不太一样,JS中一般称其为函数,我们来看看定义一个函数的格式是什么:
定义一个函数,需要在前面加上function
关键字表示这是一个函数,后面跟上函数名称和()
,其中可以包含参数,在{}
中编写函数代码。我们只需要直接使用函数名+()
就能调用函数:
我们接着来看一下,如何给函数添加形式参数以及返回值:
由于JS是动态类型,因此我们不必指明参数a的类型,同时也不必指明返回值的类型,一个函数可能返回不同类型的结果,因此直接编写return语句即可。同理,我们可以在调用函数时,不传参,那么默认会使用undefined:
那么如果我们希望不传参的时候使用我们自定义的默认值呢?
我们可以直接在形参后面指定默认值。
函数本身也是一种类型,他可以被变量接收,所有函数类型的变量,也可以直接被调用:
我们也可以直接将匿名函数赋值给变量:
既然函数是一种类型,那么函数也能作为一个参数进行传递:
对于所有的匿名函数,可以像Java的匿名接口实现一样编写lambda表达式:
和java中的其实用法一样
public static void main(String[] args){
test(str -> {
System.out.println("接收到回调参数: " + str);
});
}
interface Test{
void f(String str);
}
private static void test(Test t){
t.f("这个是回调参数");
}
JavaScript数组和对象
JS中的数组定义与Java不同,它更像是Python中的列表,数组中的每个元素并不需要时同样的类型:
我们可以直接使用下标来访问:
我们一开始编写的排序算法,也是使用了数组。
数组还可以动态扩容,如果我们尝试访问超出数组长度的元素,并不会出现错误,而是得到undefined,同样的,我们也可以直接往超出数组长度的地方设置元素:
也可以使用push
和pop
来实现栈操作:
let arr = [1, "lbwnb", false, undefined, NaN]
arr.push("bbb")
console.info(arr.pop())
console.info(arr)
数组还包括一些其他的方法,这里就不一一列出了:
let arr = [1, "lbwnb", false, undefined, NaN]
arr.fill(1)
console.info(arr.map(o => {
return 'xxx'+o
}))
我们接着来看对象,JS中也能定义对象,但是这里的对象有点颠覆我们的认知:
以上两种写法都能够创建一个对象,但是更推荐使用下面的一种。
JS中的对象也是非常随意的,我们可以动态为其添加属性:
同理,我们也可以给对象动态添加一个函数:
我们可以在函数内使用this关键字来指定对象内的属性:
let name = "我是外部变量"
let obj = {}
obj.name = "我是内部变量"
obj.f = function (){
console.info("name属性为:"+this.name)
}
obj.f()
注意:如果使用lambda表达式,那么this并不会指向对象。
除了动态添加属性,我们也可以在一开始的时候指定对象内部的成员:
注意如果有多行属性,需要在属性定义后添加一个,
进行分割!
JavaScript事件
当我们点击一个页面中的按钮之后,我们希望之后能够进行登陆操作,或是执行一些JS代码来实现某些功能,那么这个时候,就需要用到事件。
事件相当于一个通知,我们可以提前设定好事件发生时需要执行的内容,当事件发生时,就会执行我们预先设定好的JS代码。
事件有很多种类型,其中常用的有:
- onclick:点击事件
- oninput:内容输入事件
- onsubmit:内容提交事件
那么如何为事件添加一个动作呢?
我们可以直接为一个元素添加对应事件的属性,比如oninput
事件,我们可以直接在事件的值中编写js代码,但是注意,只能使用单引号,因为双引号用于囊括整个值。
我们也可以单独编写一个函数,当事件发生时直接调用我们的函数:
仅仅了解了事件,还不足以实现高度自定义,我们接着来看DOM。
Document对象
当网页被加载时,浏览器会创建页面的文档对象模型(Document Object Model),它将整个页面的所有元素全部映射为JS对象,这样我们就可以在JS中操纵页面中的元素。
比如我现在想要读取页面中某个输入框中的内容,那么我们就需要从DOM中获取此输入框元素的对象:
通过document对象就能够快速获取当前页面中对应的元素,并且我们也可以快速获取元素中的一些属性。
比如现在我们可以结合事件,来进行密码长度的校验,密码长度小于6则不合法,不合法的密码,会让密码框边框变红,那么首先我们先来编写一个css样式: .
: 类
.illegal-pwd
: 这是一个类选择器,表示这个样式将应用于所有HTML元素上,如果它们的class
属性值中包含了"illegal-pwd"。
border: red 1px solid !important;
:
border
: 定义了一个边框。red
: 边框的颜色是红色。1px
: 边框的宽度是1像素。solid
: 边框是实线。!important
: 这是一个CSS声明的权重修饰符。如果其他样式试图更改具有该类的元素的边框,!important
会确保这个特定的声明始终被应用,除非另有更具权重的!important
声明。
box-shadow: 0 0 5px red;
:
box-shadow
: 定义了一个框的阴影。0 0
: 定义了阴影的水平偏移和垂直偏移。两者都是0,表示阴影正好与元素的边界对齐,不向任何方向偏移。5px
: 阴影的模糊距离,使阴影有些许的模糊效果。red
: 阴影的颜色是红色。
总结:为所有带有类名 .illegal-pwd
的HTML元素添加红色边框,并为其添加红色的阴影。这通常用于指示某个输入字段(例如密码)是非法或不符合要求的。
接着我们来编写一下js代码,定义一个函数,此函数接受一个参数(元素本身的对象)检测输入的长度是否大于6,否则就将当前元素的class属性设定为css指定的class:
function checkIllegal(e) {
if(e.value.length < 6) {
e.setAttribute("class", "illegal-pwd")
}else {
e.removeAttribute("class")
}
}
最后我们将此函数绑定到oninput
事件即可,注意传入了一个this,这里的this代表的是输入框元素本身:
现在我们在输入的时候,会自动检查密码是否合法。
既然oninput本身也是一个属性,那么实际上我们可以动态进行修改:
那么,我们前面提及的window对象又是什么东西呢?
实际上Window对象范围更加广阔,它甚至直接代表了整个窗口,当然也包含我们的Document对象,我们一般通过Window对象来弹出提示框之类的东西。
发送XHR请求
JS的大致内容我们已经全部学习完成了,那么如何使用JS与后端进行交互呢?
我们知道,如果我们需要提交表单,那么我们就需要将表单的信息全部发送给我们的服务器,那么,如何发送给服务器呢?
通过使用XMLHttpRequest对象,来向服务器发送一个HTTP请求,下面是一个最简单的请求格式:
上面的例子中,我们向服务器发起了一次网络请求,但是我们请求的是百度的服务器,并且此请求的方法为GET请求。
我们现在将其绑定到一个按钮上作为事件触发:
function http() {
let xhr = new XMLHttpRequest();
xhr.open('GET', 'https://www.baidu.com');
xhr.send();
}
我们可以在网络中查看我们发起的HTTP请求并且查看请求的响应结果,比如上面的请求,会返回百度这个页面的全部HTML代码。
实际上,我们的浏览器在我们输入网址后,也会向对应网站的服务器发起一次HTTP的GET请求。
在浏览器得到页面响应后,会加载当前页面,如果当前页面还引用了其他资源文件,那么会继续向服务器发起请求,直到页面中所有的资源文件全部加载完成后,才会停止。