如何写好原生JavaScript

基础注意点

  • JavaScript负责行为改变状态,不是用来改变样式的。js:动态脚本语言。
  • CSS负责样式
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>各司其职</title><style>body{background: white;font-size: 14px;}#user-list, #user-list1 {width: 200px;line-height: 1.5em;}#user-list > li:hover{background-color: rgba(0, 0, 0, 0.7);}#user-list > li.active{background-color: red;color: white;}#user-list1 input{display: none;}#user-list1 label{display: block;}#user-list1 > li:hover{background-color: rgba(0, 0, 0, 0.3);}#user-list1 input:checked+label{background-color: black;color: white;}</style>
</head>
<body>
<ul id="user-list"><li>赵</li><li>钱</li><li>孙</li><li>李</li>
</ul>
<ul id="user-list1"><li><input type="radio" name="items" id="item0" value="zhou"><label for="item0">zhou</label></li><li><input type="radio" name="items" id="item1" value="wu"><label for="item1">wu</label></li><li><input type="radio" name="items" id="item2" value="zheng"><label for="item2">zheng</label></li><li><input type="radio" name="items" id="item3" value="wang"><label for="item3">wang</label></li>
</ul><script>// 版本二let list = document.querySelector('#user-list');let items = list.querySelectorAll('li');list.addEventListener('click', function (e) {items.forEach(function (item) {item.className = '';});if(e.target.tagName === 'LI'){let item = e.target;item.className = 'active';console.log(item.innerHTML);}});// 版本三let list1 = document.querySelector('#user-list1');list1.addEventListener('click', function (e) {if(e.target.tagName === 'INPUT'){let checkedItem = list1.querySelector('input:checked');console.log(checkedItem.value);}});</script>
</body>
</html>

  • 思考1:

    • 写JavaScript操作DOM要注意什么?
    • JavaScript与HTML、CSS的职责如何分离?
    • 把复杂性放在哪一头,为什么?
  • 思考2: API的设计?

<script>
//  版本一
//  问题:过程耦合 + Callback Hellconst traffic1 = document.getElementById('traffic1');(function reset() {traffic1.className = 'wait';setTimeout(function () {traffic1.className = 'stop';setTimeout(function () {traffic1.className = 'pass';setTimeout(reset, 2000);}, 2000)}, 2000);})();//  版本二
//  优点:着手于状态的改变
//  问题:依赖于外部变量 stateList,currentStateIndex,这两个变量应该封装起来,暴露出来。封装性不好const traffic2 = document.getElementById('traffic2');var stateList = ['wait', 'stop', 'pass'];var currentStateIndex = 0;setInterval(function () {traffic2.className = stateList[currentStateIndex];currentStateIndex = (currentStateIndex + 1) % stateList.length;}, 2000);//  版本三
//  问题:可复用性差const traffic3 = document.getElementById('traffic3');function start(traffic, stateList) {let currentStateIndex = 0;setInterval(function () {traffic3.className = stateList[currentStateIndex];currentStateIndex = (currentStateIndex+1) % stateList.length;}, 2000);}start(traffic3, ['wait','stop','pass']);//  版本四
//  过程抽象,函数式编程,抽象出 poll 方法const traffic4 = document.getElementById('traffic4');function poll(...fnList) {let stateIndex = 0;return function (...args) {let fn = fnList[stateIndex++ % fnList.length];return fn.apply(this, args);}}function setState4(state) {traffic4.className = state;}let trafficStatePoll = poll(setState.bind(null, 'wait'),setState4.bind(null, 'stop'),setState4.bind(null, 'pass'));setInterval(trafficStatePoll, 2000);//  版本五
//  优点:对等待时间抽象const traffic5 = document.getElementById('traffic5');function wait(time) {return new Promise(resolve => setTimeout(resolve, time));}function setState5(state) {traffic5.className = state;}function reset() {Promise.resolve().then(setState5.bind(null, 'wait')).then(wait.bind(null, 1000)).then(setState5.bind(null, 'stop')).then(wait.bind(null, 2000)).then(setState5.bind(null, 'pass')).then(wait.bind(null, 3000)).then(reset);}reset();//  版本六
//  形成一个协议
//  面向对象、函数式、Promise、灵活可扩展const traffic6 = document.getElementById('traffic6');function TrafficProtocol(el, reset) {this.subject = el;this.autoReset = reset;this.stateList = [];}TrafficProtocol.prototype.putState = function (fn) {this.stateList.push(fn);};TrafficProtocol.prototype.reset = function () {let subject = this.subject;this.statePromist = Promise.resolve();this.stateList.forEach((stateFn) => {this.statePromist = this.statePromist.then(()=>{return new Promise(resolve => {stateFn(subject, resolve);});});});if(this.autoReset){this.statePromist.then(this.reset.bind(this));}};TrafficProtocol.prototype.start = function () {this.reset();};var trafficE = new TrafficProtocol(traffic6, true);trafficE.putState(function (subject, next) {subject.className = 'wait';setTimeout(next, 1000);});trafficE.putState(function (subject, next) {subject.className = 'stop';setTimeout(next, 2000);});trafficE.putState(function (subject, next) {subject.className = 'pass';setTimeout(next, 3000);});trafficE.start();</script>
  • 思考3: JavaScript的“效率”?
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>面试题:在一个数组中找和为10的数组对</title>
</head>
<body>
<script>// 版本一:两层循环 O(n^2)let list1 = [11,4,9,3,-1,-3,6,7,9,13];function map1(list){let ret = [], len = list.length;for(let i=0; i<len; i++){for(let j=i+1; j<len; j++){if(list[i]+list[j]===10){ret.push([list[i],list[j]])}}}return ret;}console.log(JSON.stringify(map1(list1)));// 版本二:先排序function map2() {let ret = [];list = list.sort((a,b)=>a-b);for(let i=0, j=list.length-1; i<j;){let a = list[i], b = list[j];if(a[i]+b[j]===10){ret.push([a,b]);i++;j--;}else if(a+b<10){i++;}else{j--;}}return ret;}console.log(JSON.stringify(map(list)));
</script>
</body>
</html>
  • 小结

    • 不应该用JS直接操作DOM
    • API的设计,通用型,抽象度,可扩展性
    • 程序要用合适的算法

JavaScript概览

JavaScript 简史

JavaScript 的特点

  • 动态 + 弱类型
  • 解释型或实时编译(JIT)型语言
  • 面向对象
  • 函数式特征
  • 灵活和可扩展性
  • 反射与元编程
  • 高性能
  • 单线程异步非阻塞

JavaScript 主要能做什么

  • 通过DOM:改变网页文档元素和属性
  • 通过BOM:操作浏览器API
  • 事件机制:响应用户行为
  • XHR、Fetch、WS:发送和接受数据
  • Storage:保存数据和状态
  • Timer、Promise:执行异步任务
  • ArrayBuffer、TypedArray:处理数据
  • File API:操作文件

JavaScript 系语言

谁在使用JavaScript

  • 浏览器
  • 服务端,如 node.js
  • 硬件开发,如 树莓派
  • 操作数据库,如mongoDB、CouchDB
  • 写原生应用,如ReactNative
  • 开发游戏,如COCOS

本博客主要讲什么

  • 符合ECMA-262最新规范的JavaScript,不回避新特征
  • 基本类型、原生类型、浏览器API、Node API(服务端)
  • 大部分语言特性(特别是ESS及之前的版本在chrome40以上已完全支持)
  • 重点讲JavaScript的独有特性及其应用场景,以实际工作常见和常用的为主。
  • 重点讲函数、面向对象、过程抽象和函数式,总之能发挥JS动态特性的常见模式
  • 会涉及到语言基础、运行环境(如浏览器环境、Node)
  • 会涉及到部分后段、HTTP请求相关内容,这些内容对高端工程师高质量完成工作也是很有必要的

本博文不讲的内容

  • 过时的不符合规范的特性,比如ES3和之前版本里被废弃和修正的东西
  • 基础通用的编程语言特性,比如常见的基本if、for、while、switch语句
  • 一些稍微不那么复杂、可以自学、工作中也会用到的内置类型,比如 Date
  • 具体框架的使用,可以看文档
  • 建议阅读 MDN文档

兼容性怎么办?

  • 速查:速查JS兼容
  • 方案1:shim & polyfill
  • 方案2:library 实际项目里对于兼容IE8一下浏览器建议使用 jQuery
  • 方案3:Bable/JSX/TypeScript

代码风格

  • 代码的格式(包括缩进、大括号、分号、换行、空行、空格)
  • 广义的:
    • 目录结构和文件名
    • 变量命名
    • 文档规范
  • 一起遵守规则:
    • 2 spaces:两个空格
    • Single quotes for strings:字符串用单引号
    • No unused variables:不要有不使用的变量
    • Semicolons:写分号
    • Never start a line with ( [ '
    • No space after keywords: if 和 括号 之间没有空格
    • No space after function name:函数名和括号之间没有空格
    • Always use === instead of ==:用三个等号判断

变量、值与类型

关于类型

关于JS类型的几点说明

  • JS是 动态类型 + 弱类型 的语言
  • JS的变量、属性在运行期间决定类型
  • JS存在隐式类型转换
  • JS有一系列识别类型的反射方法

ECMA的语言类型

  • typeof
  • A function is a callable object

变量声明

  • 根据规范,JS有三种变量声明的方法:

    • var:声明一个变量,可选择将其初始化为一个值
    • let:声明一个块级作用域变量,可选择将其初始化为一个值。(ES6)
    • const:声明一个只读的常量.(ES5)
  • 使用var 的注意事项:

    • var不支持块级作用域
    • var 存在变量提升(Variable hoisting)。变量提升作用域。如:var a=10; 相当于 var a; a=10;
  • 使用let 的注意事项:

    • 块级作用域。出了块就是undefined
    • 同一作用域中不允许重复声明。
    • 暂存死区(Temporal dead zone,TDZ)。在一个变量a声明之前使用该变量会报错ReferenceError:a is not defined,不会有变量提升,且typeof(a)undefined
    • 循环中的let 作用域。
      • let 有词法作用域。
      • 实例:
    //  版本一//  会因变量提升而出问题var buttons = document.getElementsByTagName('button');for(var i=0; i<buttons.length; i++){buttons[i].onclick =evt => console.log('你点击了第' + i + '个按钮!')}//  版本二:利用let的块级作用域for(let i=0; i<buttons.length; i++){buttons[i].onclick =evt => console.log('你点击了第' + i + '个按钮!')}//  版本三:利用闭包的特性解决var buttons = document.querySelectorAll('button');for(var i=0; i<buttons.length; i++){(function (i) {buttons[i].onclick =evt => console.log('你点击了第' + i + '个按钮!');})(i);}




- 浏览器兼容性 。

  • 使用const 的注意事项

    • const 声明的标识符所绑定的值不可以再次改变引用。但是还是可以修改对象里的内容,若想不允许修改可以使用Object.freeze({...})
    • ES6 const 的其他行为和let 一样

如果不声明会怎样?

  • 严格模式:use strict。会报错ReferenceError: a is not defined
  • 非严格模式:不会报错

抽象相等

  • 严格相等与非严格相等

    • null == undefined null !== undefined
    • 非严格比较:会进行类型转换
    • number 里有个特殊的取值NaNNaN 与任何值(包括自身)都不想等

Boolean 类型

  • 只有两个取值:truefalse
  • 0' 'nullundefined 被隐式地转换为false,其他转为true
  • 建议采用严格比较,可以通过!! 讲非boolean 值转为boolean 值。
  • 布尔操作符&&|| 并不会转换类型(尤其注意!!)
  • 如:
  • &&|| 的短路特性
  • 比较操作符总是返回boolean 类型
  • Boolean 类型的运用:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>小动画</title><style>div{width: 50px;height: 50px;background: red;border: 1px solid red;border-radius: 50%;}</style>
</head>
<body>
<div class="ball"></div>
<script>function applyAnimate(el, duration, options, easing) {var startTime = Date.now();if(typeof el === 'string'){el = document.querySelector(el);}duration = duration || 1000;options = options || {property: 'x',distance: 100};easing = easing || function(p){return p;};requestAnimationFrame(function update() {var now = Date.now();var p = (now - startTime)/duration;var ep = easing(p);if(typeof options !== 'function'){var attr = options.property, distance = options.distance;var translate = [];if(attr.indexOf('x') >= 0){translate.push('translateX(' + distance * ep + 'px)');}if(attr.indexOf('y') >= 0){translate.push('translateY(' + distance * ep + 'px)');}el.style.transform = translate.join(' ');}else{options(el, ep, p);}if(p <= 1){requestAnimationFrame(update);}});}document.querySelector('.ball').onclick = function () {applyAnimate('.ball');}
</script>
</body>
</html>

Number 类型

  • 数值范围:

    • 整数:-2^53 ~ 2^53
    • 小数精度
  • Number.EPSILONInfinity 无穷大、Number.MAX_VALUENumber.MIN_VALUE
  • 浮点数精度问题
  • 二进制、八机制、十进制、十六进制
  • +0 和 -0

String

  • 引号规范。在JS中的字符串建议用单引号。
  • 转义字符
  • 字符串与字符
    • 字符串可以拆成字符数组
    • var charArray = Array.from(str);var charArray = str.split('');
    • str.charCodeAt(4)
    • String.fromCharCode(116, 103)
  • 字符串与类型转换
    • -
  • 常用字符串操作
    • 字符串查找和替换:str.indexOf(subStr)
    • 字符串大小写:str.toUpperCase();, str.toLowerCase();
    • 字符串连接:str1+''+str2
    • 截取子串:str.slice(起始位置,结束位置); str.substr(起始位置,长度);
      • 逆序字符串:str.split('').reverse.ioin('');
    • 字符串匹配:
  • 模版字符串

Object

  • 对象的属性

    • 属性名规则:可以是有效字符串或者任意可转换为有效字符串的类型
    • 属性的访问和遍历
  • 值和引用
    • 值类型与引用类型
    • 基本类型对应的包装类型(不带new的是值类型,带new的是包装类型)
    • 对象的拷贝
  • 类型与构造器
    • newconstructor
    • prototype 原型链(一层层往上找某 属性)
    • instanceOf
    • ES6 class
  • 内置类型
    • Object:根
    • Function:JS中最核心的
    • Promise:处理异步
    • Array:数组
    • Date:日期
    • Regex:正则表达式
    • Error
    • Math:数学
    • ArrayBuffer
    • DataView
    • Map:数据类型
    • Set:数据类型,用来去重
    • TypedArray:数据类型
    • Proxy
  • 对象的高级属性
    • 为什么不建议修改Object.protytypeArray.prototype 呢?在类型(或对象)上添加方法的代价,会被所有的实例所继承下来。解决方法:Object.defineProperty,不会被for in 检索出来,若想让其检索出来需要加enumerable:true;
    • gettersetter
    • 属性描述符
    • 数据绑定视图
  • class(未)
  • Symbol(未)
    • ES6新特性
    • 生成唯一表示
    • 用作对象的key
    • Symbol.for
    • Symbol 的基本用法
    • 系统设置Symbol

函数

函数声明、函数表达式

  • 注意函数声明提升
  • 实例:函数声明提升,变量声明提升,但是此时并未赋值
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>函数声明提升与赋值</title>
</head>
<body>
<script>console.log([typeof add, typeof sub]);function add(x, y) {return x+y;}var sub = function (x, y) {return x-y;};console.log([add(2,5), sub(2,6)]);
</script>
</body>
</html>

  • 箭头函数表达式
  • 实例:ES6新增:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>箭头函数表达式</title>
</head>
<body>
<script>let double = x => x*2;let add = (x, y) => {return x + y;};console.log([double(12), add(3,7)]);
</script>
</body>
</html>

匿名函数

  • 在函数表达式和回调函数中常见
  • 匿名函数与arguments.callee
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>匿名函数实现倒计时效果</title>
</head>
<body>
<script>function countDown(count, interval, callback) {if(count<=0) { return; }callback(count);setTimeout(function () {if(--count > 0){setTimeout(arguments.callee, interval);}callback(count);}, interval);}countDown(6, 1000, t => console.log('我是'+t));console.log('出发!');
</script>
</body>
</html>

函数参数

  • 具名的参数,function.length 是一个类数组的函数,但不是数组。
  • 可变参数与arguments
  • ES6 rest 参数,是一个真正的数组。
  • 参数默认值
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>可变参数与arguments</title>
</head>
<body>
<div style="width: 50px; height: 50px;">这里!!</div>
<p style="width: 50px; height: 50px;">这里!!</p><script>function add() {// var args = [].slice.call(arguments);  一种写法// var args = Array.prototype.slice.call(arguments);  一种写法// trueA = Array.from(FakeA) ==> 这个函数把伪数组FakeA 转换成数组 TrueA。let args = Array.from(arguments);  //  一种写法return args.reduce((a,b) => a+b);}console.log(add(11,2,3,4));//  javascript 函数设计中经常会让参数允许有不同的类型//  el: element   给element的某个属性property赋值valuefunction setStyle(el, property, value) {if(typeof el === 'string'){el = document.querySelector(el);}if(typeof property === 'object'){for(var key in property){setStyle(el, key, property[key]);}}else{el.style[property] = value;}}console.log(setStyle.length);setStyle('div', 'background', 'red');setStyle('p', {'fontSize':'16px','backgroundColor':'blue'});//  ES6:rest参数//  ...args 这其实就是一个数组let add6 = (...args) => args.reduce((a,b) => a+b);console.log(add(1,2,3,4));console.log(add.length);function deepCopy(des, src) {if(!src || typeof src !== 'object'){return des;}for(var key in src){let obj = src[key];if(obj && typeof obj === 'object'){des[key] = des[key] || {};deepCopy(des[key], obj);}else{des[key] = src[key];}}return des;}function merge(des, ...objs) {return [des, ...objs].reduce((a,b) => deepCopy(a,b))}console.log('ES6 rest参数:' + JSON.stringify(merge({x:1}, {y:2}, {z:3})));</script>
</body>
</html>

作用域、闭包、this

  • 重要:ES5用函数来构建作用域

    • ES没有块级作用域
  • IIFE:立即执行函数
  • 什么是闭包

    • 实例一
    • 实例二
  • 实例一:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>闭包作用域</title>
</head>
<body>
<script>//  因为变量提升而导致所有时候:i=5for(var i=0; i<5; i++){setTimeout(function () {console.log(i);}, 100);}//  方法一: 闭包:立即执行函数for(var i=0; i<5; i++){(function (i) {setTimeout(function () {console.log(i);}, 500);})(i);}//  方法二: ES5 bind方法for(var i=0; i<5; i++){setTimeout((function (i) {console.log(i);}).bind(null, i), 1000);}//  方法三: ES6:块级作用域for(let i=0; i<5; i++){setTimeout(function () {console.log(i);}, 2000);}</script>
</body>
</html>

  • 实例二:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>闭包与私有数据</title>
</head>
<body>
<script>//  版本一var myClass = (function () {var privateData = 'privateData';function Class() {this.publicData = 'publicData';}Class.prototype.getData = function () {return privateData;};return Class;})();var myObj = new myClass();console.log([myObj.publicData, myObj.privateData, myObj.getData()]);//  版本二:ES6 块级作用域let Class2;{let privateD = 'privateD';Class2 = function () {this.publicD = 'publicD';};Class2.prototype.getD = function () {return privateD;};}var myO = new Class2();console.log([myO.publicD, myO.privateD, myO.getD()]);</script>
</body>
</html>

applycallbind

  • JavaScript的this

    • JS的this 是由函数求值时的调用者决定的
  • apply, call, bind
    • 通过applycall 指定this 调用函数
    • ES5的bind
    • bind 的重要意义和高级用法
  • 实例:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>call, apply, bind</title><style>body{transition: all 0.5s;}body.state1{background-color: red;}body.state2{background-color: lightskyblue;}</style>
</head>
<body>
<script>function add(x, y) {return x+y;}//  callconsole.log(add.call(null, 1, 2));//  applyconsole.log(add.apply(null, [3,4]));//  bind 若将全部参数都传入,则延迟调用,会返回一个functionconsole.log(add.bind(null,  5, 6));//  bind 部分调用let add1 = add.bind(null, 7);console.log(add1(8));function setBodyState(state) {document.body.className = state;}setBodyState.call(null, 'state1');setTimeout(setBodyState.bind(null, 'state2'), 1500);</script>
</body>
</html>

关于异步回调

  • JS 的异步

    • Timer

      • 实例一
    • 事件
    • 获取数据API
    • Promise
    • 其他异步模型
      • 异步串行模型through2--gulp 使用
      • 异步串行模型connect--express、koa 使用
      • ES6 generators
      • ES2015 async/wait
      • 。。。

实例一:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>用timer异步执行</title><style>#ball{display: inline-block;background-color: gray;width: 50px;height: 50px;border-radius: 50%;text-align: center;line-height: 50px;color: white;}#ball:hover{cursor: pointer;}#ball.warn{animation: 1s 7s blink linear 3 forwards normal;}@keyframes blink {0% {background: #FFFFFF;}50% {background: #f00;}100% {background: #FFFFFF;}}</style>
</head>
<body>
<div id="ball">1</div>
<script>const ball = document.getElementById('ball');ball.addEventListener('click', function () {var startTime = Date.now();var tId = setInterval(function () {var t = 10-Math.round((Date.now()-startTime)/1000);ball.innerHTML = Math.max(t, 0);if(t<=0){clearInterval(tId);}}, 1000);ball.className = 'warn';});
</script>
</body>
</html>

函数的动态构建

  • Function 构造器与函数模版
  • 过程抽象和高阶函数
  • 过程抽象与函数式编程
    • 过程抽象的定义

      • 过程抽象与数据抽象的区别

函数式编程

  • 什么是函数式编程
  • 函数式编程与前端代码有什么关系

    • 函数的纯度和“提纯”
    • UncurringCurrying & Partial Application
  • 纯函数

    • 定义:一个函数若输入参数确定则输出结果是唯一确定的,那么它就是纯函数。
    • 好处:无状态、无副作用、幂等、无关时序。
    • 过程抽象可以提升函数纯度
  • 等价函数
    • 可以实现拦截和监控
  • 举例说明
  • 过程抽象与函数式编程
  • 函数异步化与串行执行
  • reduce同步 & pipe异步
  • throttle 避免重复点击;debounce 避免重复点击
  • multicast 批量操作DOM 元素

  • 阅读:

    • 书:《计算机程序的构造和解释》
    • JavaScript与函数式编程
    • 函数式编程术语解析
    • 什么是纯函数
    • 函数式编程离我们还有多远
    • 高阶函数对系统的“提纯”

如何封装好的函数

  • 明确职责
  • 限制副作用(尽量使用纯函数,限制操作DOM的函数的个数)
  • 过程的优化
  • 掌握抽象度

总结

  • 函数的本质:封装过程,开放接口
  • 理解过程抽象和函数式编程
  • Tip:好的程序设计方式是面向接口编程,而不是面向实现编程

DOM & BOM

  • DOM 文档树
  • DOM事件

    • 事件流
    • 默认行为
    • 事件代理

  • DOM事件有两个阶段:

    • Capture Phase:捕获阶段
    • Bubbling Phase:冒泡阶段
  • 实例一:事件流

  • 实例二:组织默认行为–点击图片放大
  • 实例三:事件代理
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>事件流</title><script src="../../../materials/jquery-3.1.1.js"></script><style>#outer{width: 200px;height: 200px;background-color: forestgreen;opacity: 0.7;display: flex;justify-content: center;align-items: center;}#inner{display: inline-block;width: 100px;height: 100px;background-color: #3df;opacity: 0.7;}#outer:hover, #inner:hover{opacity: 0.2;}</style>
</head>
<body>
<div id="outer"><div id="inner"></div>
</div>
<pre id="output">output:
</pre>
<script>const [outer, inner] = document.querySelectorAll('#outer', '#inner');const output = document.getElementById('output');function print(msg) {output.innerHTML += '\n' + msg;}[document.body, outer, inner].forEach(el=>{el.addEventListener('click', evt=>{let t = el.tagName, i = el.id;print(t + i + ' clicked!');}, false);})
</script>
</body>
</html>

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>点击放大图片</title><style>#fullview{position: absolute;left: 0;top: 0;width: 100%;height: 100%;background-color: rgba(0, 0, 0, 0.5);}#fullview.hide{display: none;}#fullview img{position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);}#fullview>a{color: white;float: right;margin-right: 12px;font-size: 16px;cursor: pointer;text-decoration: none;}</style>
</head>
<body>
<div id="imgview"><a href="../../../images/pic/00.png"><img src="../../../images/pic/00.png" width="200px"></a>
</div>
<div id="fullview" class="hide"><img src="../../../images/pic/00.png" alt="">
</div>
<script>const imageView = document.getElementById('imgview');const fullView = document.getElementById('fullview');imageView.addEventListener('click', evt=>{evt.preventDefault();fullView.className = '';});fullView.addEventListener('click', evt=>{evt.target.className = 'hide';});
</script>
</body>
</html>

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>事件代理</title><style>button{font-size: 16px;}li{cursor: pointer;user-select: none;}li.selected{color: red;}</style>
</head>
<body>
<button>new</button>
<ul><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li>
</ul>
<script>const btn = document.querySelector('button');const list = document.querySelector('ul');const items = list.getElementsByTagName('li');btn.addEventListener('click', evt=>{let item = document.createElement('li');item.innerHTML = items.length+1;list.appendChild(item);});//   不推荐这种做法,因为无法给新增的li添加事件
//   Array.from(items).forEach(item =>{//       item.addEventListener('click', evt=>{//           evt.target.className = evt.target.className?'':'selected';
//       });
//   });//    推荐做法list.addEventListener('click', evt=>{let el = evt.target;if(el.tagName === 'LI'){el.className = el.className?'':'selected';}});</script>
</body>
</html>

  • 宽高和定位

    • clientWidthclientHeight:内容的容器内可见宽高

    • offsetWidthoffsetHeight:内容的盒子(不包括margin)宽高
    • scrollWidthscrollHeight:内容真正的宽高
    • 实例:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>宽高和定位</title><style>html, body {margin: 0;padding: 0;}p {margin: 0;padding: 0;}#outer {display: inline-block;width: 200px;height: 200px;margin: 10px;background: gray;text-align: center;border: 5px solid lightseagreen;}#inner {display: inline-block;width: 100px;height: 100%;background: lightcoral;overflow: scroll;border: 5px solid lightseagreen;}</style>
</head>
<body>
<p id="atext">This is a paragraph</p>
<div id="outer"><div id="inner"><p>aaa</p><p>aaa</p><p>aaa</p><p>aaa</p><p>aaa</p><p>aaa</p><p>aaa</p><p>aaa</p><p>aaa</p><p>aaa</p><p>aaa</p><p>aaa</p><p>aaa</p><p>aaa</p><p>aaa</p><p>aaa</p><p>aaa</p></div>
</div>
<script>setTimeout(function(){//  无法获取继承的值const p = document.getElementById('atext');console.log('width' + 'height' +JSON.stringify([p.style.width, p.style.height]));//  通过getComputedStyleconst styleP = window.getComputedStyle(p);console.log('width' + 'height' + JSON.stringify([styleP.width, styleP.height]));console.log('clientWidth ' + 'clientHeight ' + JSON.stringify([p.clientWidth, p.clientHeight]));console.log('offsetWidth ' + 'offsetHeight ' + JSON.stringify([p.offsetWidth, p.offsetHeight]));console.log('scrollWidth ' + 'scrollHeight ' + JSON.stringify([p.scrollWidth, p.scrollHeight]));console.log('---------');console.log('clientWidth ' + 'clientHeight ' + JSON.stringify([outer.clientWidth, outer.clientHeight]));console.log('offsetWidth ' + 'offsetHeight ' + JSON.stringify([outer.offsetWidth, outer.offsetHeight]));console.log('scrollWidth ' + 'scrollHeight ' + JSON.stringify([outer.scrollWidth, outer.scrollHeight]));console.log('---------');console.log('clientWidth ' + 'clientHeight ' + JSON.stringify([inner.clientWidth, inner.clientHeight]));console.log('offsetWidth ' + 'offsetHeight ' + JSON.stringify([inner.offsetWidth, inner.offsetHeight]));console.log('scrollWidth ' + 'scrollHeight ' + JSON.stringify([inner.scrollWidth, inner.scrollHeight]));}, 100);</script>
</body>
</html>

  • UI组件设计

    • 结构设计
    • API设计
    • 控制流设计
    • 实例:图片轮播
  • 图片轮播
  • -

动画基础(上)

动画基础(下)

服务器与HTTP基础

常用设计模式和组件开发

浅谈前端工程化

奇舞学院学习笔记之JavaScript一页通相关推荐

  1. 奇舞学院学习笔记之CSS一页通

    CSS概念与简单选择器 版本 CSS Level 1 CSS Level 2(CSS2.1规范) CSS Level 3 Color Module Level 3 Selectors Level 3 ...

  2. 尚硅谷谷粒学院学习笔记(防坑点的总结部分勘误)

    谷粒学院学习笔记 部分勘误 数据库设计规约 模块说明 环境搭建 创建一个Spring Boot 的父工程,版本使用:2.2.1.RELEASE 父工程pom.xml里面添加 在pom.xml中添加依赖 ...

  3. 【百度前端学院学习笔记】Day6 浮动/BFC

    [百度前端学院学习笔记]Day6 浮动/BFC 一.什么是浮动? 二.普通流 / 浮动 / 绝对定位 三.BFC/Flow Root 3.1 什么是BFC? 3.2 BFC 的特性 3.2.1 特性一 ...

  4. 【百度前端学院学习笔记】Day9 圣杯布局和双飞翼布局

    [百度前端学院学习笔记]Day9 圣杯布局和双飞翼布局 圣杯布局(古老而费解的方法) 双飞翼布局(圣杯的改进) 参考资料: In search of the Holy Grail - A list a ...

  5. GEE(Google Earth Engine) 最基础代码学习笔记二 —— JavaScript 语言

    GEE(Google Earth Engine) 学习笔记二 Javascript 语言 1. 注释 print('Hello World!'); 如果要注释,则在代码前面加//,比如: // pri ...

  6. Windows保护模式学习笔记(八)—— 页目录表基址/页表基址

    Windows保护模式学习笔记(八)-- 页目录表基址/页表基址 要点回顾 一.页目录表基址 实验:拆分线性地址C0300000,并查看其对应的物理页 第一步:打开一个进程,获得它的Cr3 第二步:查 ...

  7. 奇舞学院JavaScript视频-如何写好原生js

    月影JavaScript视频学习笔记 第零课 Q1: 列表渲染的不同版本 优劣 版本1(初级前端) let list = document.querySelector('#user-list'); l ...

  8. 前端学习笔记之 JavaScript WebAPIs(整理)

    目录 篇一 一.鼠标事件 二.获取元素的属性值 三.设置元素的属性值 四.移除属性 五.H5自定义属性 1. 设置自定义属性 2. 获取自定义属性 六.节点操作 1.两种方法的区别 2.节点概述 3 ...

  9. 【前端学习笔记】JavaScript + jQuery + Vue.js + Element-UI

    前端学习笔记 JavaScript jQuery Vue.js Element-UI Java 后端部分的笔记:Java 后端笔记 JavaScript 基础语法(数据类型.字符串.数组.对象.Map ...

最新文章

  1. 【干货】裸金属服务Ironic项目介绍
  2. git中Please enter a commit message to explain why this merge is necessary.
  3. 京东AI一把手周伯文被曝离职创业,曾任技术委员会主席,毕业于中科大少年班...
  4. C# 入门经典 第三版 下载。
  5. 7.7-9 chage、chpasswd、su
  6. 蓝桥杯 平面切分(欧拉定理)
  7. 组态王怎么做超级曲线_鸭肉怎么做?大叔教你红烧鸭块,香气扑鼻,简单易做,超级好吃...
  8. Android 为应用添加默认加载页
  9. java 发送带basic认证的http post请求实例代码_图解HTTP学习笔记(八)—确认访问用户身份的认证...
  10. 输入控件控制输入限制
  11. 一起谈.NET技术,Microsoft NLayerApp案例理论与实践 - 多层架构与应用系统设计原则...
  12. SAP ABAP开发从入门到精通——第15章 面向对象ALV
  13. linux修改无线网卡hwaddr,在Linux下改无线网卡的mac的地址
  14. java version什么意思_输入java -version命令后提示结果如下,是什么意思??哪位能看懂,在线等。。。。...
  15. 重磅:Mobileye官宣推迟IPO,营收增速放缓、市场竞争加剧
  16. 0x5085170C (ucrtbased.dll)处(位于 Project1.exe 中)引发的异常: 0xC0000005: 读取位置 0x0000001A 时发生访问冲突。
  17. RabbitMQ - 4种Exchange类型
  18. Markdown设置字体大小、颜色、类型、加粗
  19. Spring - bean
  20. Linux ps命令详解,Linux查看进程

热门文章

  1. [note] 拉-曼-Raman spectra
  2. android 适配最佳方案,android适配方案
  3. Intellij Idea 快捷键设置大全
  4. C++学习记录之STL函数
  5. MySQL的50条经典语句(更新中...
  6. 旭凤锦覓虐心 恋只愿共赴鸿蒙,《香蜜》原著锦觅和旭凤的“虐恋情深”:细想,他们的爱情,不美...
  7. 查看Linux操作系统版本及内核版本
  8. H.264裸流文件中获取每一帧数据
  9. PXE+VMware主机模式+KickStart脚本 自动安装ESXi 并试用WireShark抓包分析其中的协议TFTP,DHCP
  10. 滴滴杜欢:大型微服务框架设计实践