本文不定期优化更新。均以格式展开:定义、作用、原理、应用、优缺点、更新
目录
概述
JavaScript严格区分大小写
- 应用场景
表单动态校验(密码强度)
网页特效
服务端开发 Node.js
桌面程序 Electron
App
控制硬件-物联网 Ruff
游戏开发 cocos2d-js - javaScript(JS)包括:
- ECMAScript是JS的语言标准 如ES6
- DOM文档对象模型
- BOM浏览器对象模型,包含window,location,screen,history…
引入
1、内部
放入<head>
<script type="text/javascript">js code</script>
2、外部:js文件引入 (更方便维护)
<script type="text/javascript" src="..."></script>
运行条件
必须先有HTML页面,在HTML页面中引入JavaScript,浏览器加载该HTML页面,执行JavaScript代码。
注释 // /* */
废弃特性
很多类似这种特性都被废除,比如字符串对象,都是不再将被处理的对象作为参数传递,而是用实例其自带的方法调用。
❌ 非标准旧写法 | ✅ 推荐的标准写法 |
---|---|
Array.slice(myArr, 0, 2) |
myArr.slice(0, 2) |
Array.forEach(myArr, fn) |
myArr.forEach(fn) |
Array.map(myArr, fn) |
myArr.map(fn) |
Array.filter(myArr, fn) |
myArr.filter(fn) |
Array.join(myArr, ',') |
myArr.join(',') |
JS 核心基础
运行机制
- JS是单线程,不能同时进行;因为 JavaScript 是为处理页面中用户的交互,以及操作 DOM 而诞生的。比如我们对
某个 DOM 元素进行添加和删除操作,不能同时进行。 应该先进行添加,之后再删除。如果 JS 执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉。 - 同步任务v.s.异步任务
为了解决这个问题,利用多核 CPU 的计算能力,HTML5 提出 允许 JavaScript 脚本创建多个线程,但是子线程完全受主线程控制。故出现了JS中所有任务可以分成两种,同步任务(synchronous)和异步任务(asynchronous)。
同步任务指的是:在主线程【执行栈】上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;
异步任务指的是:不进入主线程、而进入【任务队列】的任务,当主线程中的任务运行完了,才会从”任务队列”取出异步任务放入主线程执行。
比如你在做一件事情时,因为这件事情会花费很长时间,在做这件事的同时,你还可以去处理其他事情。比如做饭的异步做法,我们在烧水的同时,利用这10分钟,去切菜,炒菜。
【异步任务】类别 | 示例/说明 |
---|---|
网络请求 | fetch(), axios.get(), XMLHttpRequest |
定时器任务 | setTimeout, setInterval, requestAnimationFrame |
DOM 事件回调 | click, keyup, onload 等事件回调 |
Promise 任务 | .then, .catch, .finally |
Async/Await | 封装 Promise 的更简洁语法 |
微任务(Microtasks) | queueMicrotask, Promise.then |
宏任务(Macrotasks) | setTimeout, setInterval, MessageChannel |
Web Worker | 在子线程中处理数据(比如大计算任务) |
事件循环触发 | 浏览器/Node 的 event loop 会安排任务执行顺序 |
Web Worker
- 定义:浏览器HTML5提供多线程技术,允许在主线程之外创建后台线程,使用Web Worker可以将费时的JS任务从主线程中分离出来,让主线程专注于处理UI和用户交互等任务。避免页面卡顿,提升用户体验。
- 特点:浏览器中的独立线程,运行在主线程之外;不能访问 DOM 和大部分浏览器 API;只能通过消息传递与主线程通信
- 类型
Dedicated Worker(专用工作线程):一个 Worker 只服务于创建它的页面
Shared Worker(共享工作线程):可以被多个脚本共享(较少用) - 使用
//main.js 主线程
const worker = new Worker('worker.js');//1. 创建实例
worker.postMessage('开始计算');
worker.onmessage = function(event) {
console.log('Worker 返回:', event.data);
};
worker.terminate();//主线程使用则关闭webWorker
//worker.js
//onmessage接收主线程消息的事件处理函数 同时监听
//postMessage发送消息给主线程 主线程发送给webWorker都用这个
self.onmessage = function(event) {
//2. 定义任务
// 执行耗时任务
let result = 0;
for (let i = 0; i < 1e7; i++) {
result += i;
}
self.postMessage(result); // 发送结果回主线程
};
- 与service worker异同
//sw.js
self.addEventListener('fetch', event => {
// 拦截请求,实现缓存策略
});
- 共同点:
都是浏览器提供的多线程技术
都在主线程之外运行,避免阻塞页面的 UI 渲染。
都不能直接访问 DOM,出于线程安全和性能考虑,两个 Worker 线程都无法操作页面 DOM。
都运行在独立的线程环境,线程独立于主线程,互不干扰,提高性能和响应能力。
通信机制基于消息传递,使用 postMessage 进行数据交互(Service Worker 通过事件监听机制间接实现,Web Worker 直接用 postMessage)。
都是异步执行,工作线程中的代码不会阻塞主线程,任务执行是异步的。
都受浏览器安全限制,都需要在安全上下文中运行(Service Worker 必须在 HTTPS 下,Web Worker 在页面上下文但通常也受安全策略限制)。
都是用来提升网页性能和用户体验的技术
通过分离主线程任务,避免页面卡顿和提高响应速度。 - 对比
方面 | Web Worker | Service Worker |
---|---|---|
主要作用 | 多线程运行 JS 代码,处理计算任务 | 拦截网络请求,实现离线缓存与后台功能 |
生命周期 | 页面绑定,页面关闭即终止 | 独立于页面,可长期存在 |
访问 DOM | 不可访问 | 不可访问 |
通信方式 | 主线程与 Worker 通过 postMessage 进行双向通信 |
事件驱动,监听 fetch 、push 等事件 |
安全限制 | 无需 HTTPS(任意页面均可用) | 只能在 HTTPS 环境下使用 |
主要应用场景 | 复杂计算、数据处理、图像处理、数据库/文件操作 | 离线缓存、PWA、后台推送、网络代理 |
创建方式 | 通过 new Worker() 创建 |
通过 navigator.serviceWorker.register() 注册 |
线程性质 | 独立的 JS 线程 | 独立的后台线程(生命周期更长) |
持久性 | 页面关闭即销毁 | 可能长期驻留,独立于任何页面 |
网络请求拦截 | 不支持 | 支持(可控制页面的请求响应) |
事件循环
JavaScript在按代码从前往后顺序执行,依次压入执行栈,每次执行一个方法,都会为它生成独有的执行环境(上下文),当这个方法执行完成后,就会销毁当前的执行环境,并从栈中弹出此方法,然后继续执行下一个方法。执行栈是栈,先进后出,但并不是代码从后往前执行,而是调用一个压栈、它执行完出栈,所以还是从前往后执行。
任务队列分为宏任务、微任务两种队列。详见事件循环专题 点击跳转
//下面三个片段的输出各是什么
console.log(1);
setTimeout(function() {
console.log(3);
}, 1000);
console.log(2);
//1->2->3
console.log(1);
setTimeout(function() {
console.log(3);
}, 0);
//将回调函数放入 宏任务队列,虽然 0ms 但仍然要等主线程任务执行完毕后才执行。
//即使 setTimeout 的时间是 0ms,它依然不会立即执行,而是等 同步任务执行完毕 才执行。
console.log(2);
//1->2->3
console.log(1);
document.onclick = function() {
console.log('click');
}
console.log(2);
setTimeout(function() {
console.log(3);
}, 3000);
//1 2 3(如果 3 秒不点击)
// 1 2 click 3(如果 3 秒内点击)
变量/作用域
- 声明变量方式对比
关键词 | 可重复声明 | 可重新赋值 | 块级作用域 | 是否提升(hoist) |
---|---|---|---|---|
var |
是 | 是 | ❌ 无(函数作用域) | 是(值为 undefined ) |
let |
❌ 否 | 是 | 是 | ❌ 不会提升 |
const |
❌ 否 | ❌ 否(值不能改) | 是 | ❌ 不会提升 |
- TDZ Temporal Dead Zone(暂时性死区)
定义:在变量 let / const 声明之前的区域内访问它,会抛出错误 - 作用域对比
对比项 | 函数作用域 (var ) |
块级作用域 (let/const ) |
---|---|---|
是否提升 | 提升,值为 undefined | ❌ 不提升,TDZ 阻止访问 |
作用范围 | 整个函数内 | 仅限当前 {} 内 |
重复声明 | 允许(不推荐) | ❌ 不允许 |
是否存在 TDZ | ❌ 不存在 | 存在 |
常用于 | 老代码(兼容性) | 推荐日常开发全部用 let/const |
作用域还可分为局部、全局
只要是代码至少有一个作用域,写在函数内部的局部作用域,未写在任何函数内部即在全局作用域中;
- 全局污染
引入的js文件中存在名字相同的情况,只会选择其一,后覆盖前。
作用域链
- 执行上下文:包括变量环境、作用域链、this 绑定
- 定义:嵌套关系的作用域串联起来形成作用域链。
- 作用:在内部函数可以访问外部函数变量,用链式查找决定哪些数据能被内部函数访问。
【就近原则】本质是底层变量查找机制,优先当前函数作用域中查找变量,如果当前作用域查找不到则会依次逐级查找父级作用域直到全局作用域。
子作用域能够访问父作用域,父级作用域无法访问子级作用域
function f1() {
var num = 123;
function f2() {
console.log( num );
}
f2();
}
var num = 456;
f1();
var a = 1;
function fn1() {
var a = 2;
var b = '22';
fn2();
function fn2() {
var a = 3;
fn3();
function fn3() {
var a = 4;
console.log(a); //a的值 ?
console.log(b); //b的值 ?
}
}
}
fn1();
数据类型
JavaScript 是一种弱类型/动态语言。不用提前声明变量类型,在程序运行过程中,类型会被自动确定。
- 变量类型 7+1
-
- 基本数据类型:变量存储的是值本身
Number/String/Boolean/Undefined/Null/Symbol/Bigint
- 基本数据类型:变量存储的是值本身
-
- 引用数据类型:变量存储的仅是地址(引用),通过 new 关键字创建的对象
Object(如Array/Function/Date等)
- 引用数据类型:变量存储的仅是地址(引用),通过 new 关键字创建的对象
- NAN
NaN(Not a Number,非数)表示未定义或不可表示的值。常在浮点数运算使用。Number("123nb")
会报错。它是全局属性,属于 Number 类型,数学运算错误\类型转换失败会得到。
NaN这个特殊的Number与所有其他值都不相等,包括它自己:
NaN === NaN; // false
//唯一能判断NaN的方法是通过isNaN()函数: true
Number.isNaN(NaN);
//isNaN()用来判断一个变量是否为非数字的类型,不是数字返回true,是数字返回false
bool_num=isNaN(num)
- null v.s. undefined
null表示“空”值,和0以及空字符串’‘不同,0是一个数值,’'表示长度为0的字符串,而null表示“空”。
undefined,是只声明但未赋值变量的默认值。
未声明过的值调用会报错,ReferenceError: myVar is not defined - 不声明的全局变量 以及 strict模式
未声明的变量自动为全局变量。
ECMA推出strict模式:未申明变量就使用将导致运行错误
启用strict方法是在JavaScript代码第一行写上:‘use strict’;
不支持strict模式的浏览器会把它当做一个字符串语句执行,支持strict模式的浏览器将开启strict模式运行JavaScript。
var x=30;
var name="myy";
alert(typeof name);
console.log(name);
value=prompt(text,defaultText)//用于显示可提示用户进行输入的对话框。
document.write(x)//写在页面上
var htmlStr='<h1>'+x+'</h1>'
//undefine null NAN
var str;
console.log(str);
var variable = undefined;
console.log(variable + 'pink'); // undefinedpink
console.log(variable + 1); // NaN undefined 和数字相加 最后的结果是 NaN
// null 空值
var space = null;
console.log(space + 'pink'); // nullpink
console.log(space + 1); // 1
//几进制
num=0b1;
num=0o5;
num=0xa;
//显式变量转换
var a=Number("23");
var age = parseInt(prompt('请输入年龄:'));
var height = parseFloat(prompt('请输入身高(m):'));
var str=num.toString();//或String(num)
//支持隐式转换
var num=num+"string";//数字自动转换为字符串
num='12'-2;//见到- * / 转成数字再运算
变量最后如果在全局作用域中也没有找到,则报ReferenceError错误
这与使用 var 声明的变量不同,var 声明的变量可挂载在全局对象上,因此可以通过 window.variableName 的方式访问。
//我们每次直接调用的alert()函数其实也是window的一个变量。
window.alert('调用window.alert()');
// 给alert赋一个新函数:
window.alert = function () {
}
alert(Number.MAX_VALUE); // 1.7976931348623157e+308
alert(Number.MIN_VALUE); // 5e-324
Infinity ,代表无穷大,大于任何数值
-Infinity ,代表无穷小,小于任何数值
NaN ,Not a number,代表一个非数值
类型判断
- typeof 只能判断基础类型 + 函数,不能判断数组/对象/null 的区别(它们全是 object)
typeof [] // 👉 'object'
typeof null // 👉 'object' ⚠️(历史 bug)
typeof {
} // 👉 'object'
typeof (() => {
}) // 👉 'function'
[] instanceof Array // ✅ true
{
} instanceof Object // ✅ true
(() => {
}) instanceof Function // ✅ true
123 instanceof Number // ❌ false(原始类型不是对象)
new Number(123) instanceof Number // ✅ true
null instanceof Object // ❌ false
//获取更准确的类型判断。
Object.prototype.toString.call(typeof null) // "[object String]"
Object.prototype.toString.call(123) // '[object Number]'
Object.prototype.toString.call('abc') // '[object String]'
Object.prototype.toString.call(null) // '[object Null]'
Object.prototype.toString.call(undefined) // '[object Undefined]'
Object.prototype.toString.call([]) // '[object Array]'
Object.prototype.toString.call({
}) // '[object Object]'
Object.prototype.toString.call(() => {
}) // '[object Function]'
Object.prototype.toString.call(new Date()) // '[object Date]'
Object.prototype.toString.call(/\d+/) // '[object RegExp]'
(123).constructor === Number // true
('abc').constructor === String // true
({
}).constructor === Object // true
([]).constructor === Array // true
方法 | 能判断的类型 | 优点 | 缺点 |
---|---|---|---|
typeof |
基本类型 + 函数 | 简单快捷 | 无法区分对象/数组/null |
instanceof |
引用类型(对象/数组等) | 可判断实例归属 | 不能判断原始类型/null |
Object.prototype.toString.call |
所有类型 | ✅ 最准确 | 写法稍复杂 |
Array.isArray() |
是否为数组 | 简洁,语义清晰 | 只能判断数组 |
constructor |
判断构造函数 | 有时好用 | 不安全,可被修改 |
为什么typeof null === 'object’是对的?
JS 历史遗留 bug,底层表示为 null 的二进制是全 0,而 typeof 误判为 object。
- 类型转换
var str='121yyy32.9079478';
console.log(parseInt(str)); //12132
console.log(parseFloat(str));//type:number
console.log(Number(str));//NaN
console.log(isNaN(str));
console.log(str.toString());
String(str)
//隐式转换
console.log(""+num);//=="".concat(num);
//保留小数位数
console.log(num.toFixed(2));//返回字符串
var uri="http://.....html?name=zhangsan";
//uri编码
console.log(encodeURI(uri))
console.log(encodeURIComponent(uri))//完全解析 ? =都解析出来
//uri解码
decodeURI()
decodeURIComponent()
- Object.is(): 用来比较两个值是否相等
Object.is(+0, -0); //false
Object.is(NaN, NaN); //false
运算符
- 算数运算符
次方**
- 逻辑运算符
常用&& || !
- 三目/三元运算符
条件?真run:假run
- 比较运算符
===
对应!==
,
===不会自动转换数据类型,如果数据类型不一致,返回false,如果一致,再比较。
undefined 和 null 与其自身严格相等
let res1 = (null === null) //true
let res2 = (undefined === undefined) //true
尽量不要使用= =比较,始终坚持使用= = =比较。
==
做类型转换,再进行值的比较
let res1 = (null == undefined ); // true
let res2 = (null === undefined); // false
!=
和!==
和!===
!=
是非严格不等于,允许类型转换。
!==
是严格不等于,不允许类型转换。
!===
是无效的运算符,不能使用。
- 赋值运算符
c+=5;
c++;
1、11 + “12”+ 13 的结果是什么? “111213”
2、true + false + 1 + “”的结果是什么?2
上述两个都会强行转换成数字类型。
console.log(parseInt("123.321test")); // 结果:123
console.log(parseFloat("123.321test")); // 结果:123.321
//parseInt 从字符串开头开始解析整数部分,遇到 .(小数点)后会停止解析。
//parseFloat 解析浮点数,包括小数部分,直到遇到非数值字符。
- 一元正号(Unary plus)将一个值强制转换为数字类型(Number):就是在值前面放
+
??
空值合并运算符(Nullish Coalescing)
作用:当左边是 null 或 undefined 时,才返回右边的值。?.
可选链操作符(Optional Chaining)
作用:在访问对象深层属性时,如果某一层为 null 或 undefined,不会报错,而是返回 undefined
流程控制
- 循环
for /while /do…while
for(let j=0;j<s.length;j++){
}
while(xx){
}
do {
... } while()//先执行一次 再判断
continue
break
条件判断
if(xx){
...
}else if(xx){
...
}else{
...
}
//三元运算符
condition?ok_then:no_then
switch(){
case label1:
xxx
break;
case label2:
xxx
break;
default:
xxx
break;
}
函数
//函数声明 函数提升至作用域顶部
function add(x, y) {
return x + y;
}
//函数表达式
// 函数表达式必须要先声明再调用
const add = function(x, y) {
return x + y;
};
//箭头函数
const add = (x, y) => x + y;
//默认参数
function greet(name = '游客') {
return '你好,' + name;
}
//剩余参数 变成数组
function sum(...args) {
return args.reduce((a, b) => a + b, 0);
}
sum(1, 2, 3); // 6
//返回值不写则返回undefined
- 箭头函数 v.s. 普通函数
特性 | 普通函数 (function ) |
箭头函数 (=> ) |
---|---|---|
语法 | 冗长 | 简洁 |
this 绑定方式 |
动态,谁调用指向谁 | 静态,指向定义时外部作用域 |
可作为构造函数使用 | 是 | ❌ 否 |
是否有 arguments |
有 | ❌ 没有 |
是否有 super / new.target |
有 | ❌ 没有 |
- 总结
- 函数内部声明的变量,在函数外部无法被访问
- 函数参数也是函数内部的局部变量
- 不同函数内部声明的变量无法互相访问
- 函数执行完毕后,函数内部变量实际被清空
function func(parameter){
return parameter;
//return 只能返回一个值,如果有多个则返回最后一个
//无返回时是undefined
}
var func=function(a,b){
return a+b;}
var r=func(2,3);
// 函数形参实参个数匹配
function getSum(num1, num2) {
console.log(num1 + num2);
// 1. 如果实参的个数和形参的个数一致 则正常输出结果
getSum(1, 2);
// 2. 如果实参的个数多于形参的个数 会取到形参的个数
getSum(1, 2, 3);
// 3. 如果实参的个数小于形参的个数 多于的形参定义为undefined 最终的结果就是 NaN
// 形参可以看做是不用声明的变量 num2 是一个变量但是没有接受值 结果就是undefined
getSum(1); // NaN
// 建议 我们尽量让实参的个数和形参相匹配
- IIFE(immediately invoked function expression)立即执行函数:创建闭包,保存全局作用域(window)和当前函数的作用域,因此可输出全局变量。是一种自执行匿名函数,匿名函数拥有独立作用域。不仅避免外界访问此 IIFE 中的变量,而且又不会污染全局作用域。
return坑
function foo() {
return
{
name: 'foo' };
}
foo(); // undefined
由于JavaScript引擎在行末自动添加分号的机制,上面的代码实际上变成了
function foo() {
return; // 自动添加了分号,相当于return undefined;
{
name: 'foo' }; // 这行语句已经没法执行到了
}
所以正确写法为
function foo() {
return {
// 这里不会自动加分号,因为{表示语句尚未结束
name: 'foo'
};
}
输入输出
环境 | 输入方式 | 示例 |
---|---|---|
浏览器 | prompt() |
prompt("请输入") |
浏览器 | HTML + JS | input.value + button.onclick |
Node.js | readline |
rl.question(...) |
- 在 Node.js 中获取输入
const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.question("请输入你的名字:", function(name) {
console.log(`你好,${
name}!`);
rl.close();
});
- json
fetch("data.json")
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error("加载数据失败", error));
async function fetchData() {
try {
const response = await fetch("https://jsonplaceholder.typicode.com/users/1");
const data = await response.json();
console.log(data);
} catch (error) {
console.error("请求失败", error);
}
}
fetchData();
//ES6 import
import userData from "./data.json" assert {
type: "json" };
console.log(userData);
//main.js
import {
user, getUser } from "./data.js";
console.log(user);
console.log(getUser());
//data.js
export const user = {
name: "张三", age: 25 };
export function getUser() {
return user;
}
- 读取本地存储 (localStorage / sessionStorage)
// 存储数据
localStorage.setItem("user", JSON.stringify({
name: "张三", age: 25 }));
//JSON.stringify converts a JavaScript value to a JSON string
// 读取数据
const userData = JSON.parse(localStorage.getItem("user"));
console.log(userData);
- txt,csv
浏览器的 File 对象(即 event.target.files[0])不能直接访问内容,必须通过 FileReader 读取。
FileReader 是浏览器提供的 API,用于异步读取文件内容,它支持以下格式:
readAsText(file) —— 读取文本(如 .txt, .csv, .json)。
readAsDataURL(file) —— 读取图片并返回 Base64 编码。
readAsArrayBuffer(file) —— 读取二进制数据。
readAsBinaryString(file) —— 读取二进制字符串(已废弃)。
为什么必须写 onload?
readAsText(file) 是异步操作,读取文件需要时间,所以不能直接在下一行获取 result。必须等 onload 事件触发,文件数据才会被正确读取。
1️⃣ dragover 事件
当文件被拖动到 dropArea 上方时触发。
默认情况下,浏览器不允许放置文件,e.preventDefault(); 使其成为可拖拽区域。
2️⃣ drop 事件
当文件被放下时触发。
默认行为是打开文件,但我们希望自己控制读取文件,因此 e.preventDefault(); 阻止这个默认行为。
可将读取到的文本转换成json
fetch("./data.txt") // 读取 txt 文件
.then(res => res.text()) // 读取文本内容
.then(text => {
try {
const jsonData = JSON.parse(text); // 将文本转换为 JSON
console.log(jsonData); // 输出 JSON 数据
} catch (error) {
console.error("JSON 解析失败:", error);
}
})
.catch(err => console.error("读取文件失败:", err));
<!-- 1
不适用于:直接打开 HTML 文件时(因浏览器安全限制)
-->
fetch("example.txt")
.then(response => response.text())
.then(data => console.log(data))
.catch(error => console.error("读取文件失败", error));
<!-- 2 -->
<input type="file" id="fileInput">
<script>
document.getElementById("fileInput").addEventListener("change", function(event) {
const file = event.target.files[0];
const reader = new FileReader();
reader.onload = function(e) {
console.log(e.target.result); // 读取的文件内容
};
reader.readAsText(file);
});
</script>
<!-- 3 -->
<div id="dropArea" style="width: 300px; height: 150px; border: 2px dashed gray; text-align: center; line-height: 150px;">
拖拽 TXT 文件到此处
</div>
<pre id="fileContent"></pre>
pre是一个 HTML 标签,常用于显示文本内容,尤其是保留换行和空格格式的文本。
<script>
const dropArea = document.getElementById("dropArea");
dropArea.addEventListener("dragover", function(e) {
e.preventDefault(); // 允许拖拽
});
dropArea.addEventListener("drop", function(e) {
e.preventDefault();
const file = e.dataTransfer.files[0]; // 获取拖拽的文件
if (!file || file.type !== "text/plain") {
alert("请选择一个 TXT 文件!");
return;
}
const reader = new FileReader();
reader.onload = function(event) {
document.getElementById("fileContent").textContent = event.target.result;
};
reader.readAsText(file);
});
</script>
- 读取数据库
fetch("https://example.com/api/users")
.then(res => res.json())
.then(data => console.log(data));
操作文件
在HTML表单中,上传文件唯一控件是< input type=“file”>,表单enctype必须指定为multipart/form-data,method必须指定为post,浏览器才能正确编码并以multipart/form-data格式发送表单数据。
出于安全考虑,浏览器只允许用户选择本地文件,用JavaScript对< input type=“file”>的value赋值是没有任何效果的。
JavaScript可以在提交表单时对文件扩展名做检查,以便防止用户上传无效格式的文件。
var f = document.getElementById('test-file-upload');
var filename = f.value; // 'C:\fakepath\test.png'
if (!filename || !(filename.endsWith('.jpg') || filename.endsWith('.png') || filename.endsWith('.gif'))) {
alert('Can only upload image file.');
return false;
}
HTML5新增的File API允许JavaScript读取文件内容,获得更多文件信息。提供File和FileReader两个主要对象,可获得文件信息并读取文件。
var
fileInput = document.getElementById('test-image-file'),
info = document.getElementById('test-file-info'),
preview = document.getElementById('test-image-preview');
// 监听change事件:
fileInput.addEventListener('change', function () {
// 清除背景图片:
preview.style.backgroundImage = '';
// 检查文件是否选择:
if (!fileInput.value) {
info.innerHTML = '没有选择文件';
return;
}
// 获取File引用:
var file = fileInput.files[0];
// 获取File信息:
info.innerHTML = '文件: ' + file.name + '<br>' +
'大小: ' + file.size + '<br>' +
'修改: ' + file.lastModifiedDate;
if (file.type !== 'image/jpeg' && file.type !== 'image/png' && file.type !== 'image/gif') {
alert('不是有效的图片文件!');
return;
}
// 读取文件:
var reader = new FileReader();
// FileReader() 返回一个新构造的FileReader。
//当 FileReader 读取文件的方式为
//readAsArrayBuffer,
//readAsBinaryString,
//readAsDataURL 或者 readAsText 的时候,会触发一个 load 事件。从而可以使用 FileReader.onload 属性对该事件进行处理。
reader.onload = function(e) {
var data = e.target.result;
// 'data:image/jpeg;base64,/9j/4AAQSk...(base64编码)...'
preview.style.backgroundImage = 'url(' + data + ')';
};
// 以DataURL的形式读取文件:
reader.readAsDataURL(file);
//readAsDataURL 方法会读取指定的 Blob 或 File 对象。
//读取操作完成的时候,readyState 变成已完成DONE,并触发 loadend 事件,
//result属性将包含一个data:URL格式的字符串(base64编码)以表示所读取文件的内容。
});
*严格模式
严格模式在 IE10 以上版本的浏览器中才会被支持,旧版本浏览器中会被忽略。
严格模式对正常的 JavaScript 语义做了一些更改:
1.消除了 Javascript 语法的一些不合理、不严谨之处,减少了一些怪异行为。
2.消除代码运行的一些不安全之处,保证代码运行的安全。
3.提高编译器效率,增加运行速度。
4.禁用了在 ECMAScript 的未来版本中可能会定义的一些语法,为未来新版本的 Javascript 做好铺垫。比如一些保留字如:class,enum,export, extends, import, super 不能做变量名
- 开启严格模式
严格模式可以应用到整个脚本或个别函数中。因此在使用时,我们可以将严格模式分为脚本开启严格模式和为函数开启严格模式两种情况。
- 脚本开启严格模式
有的 script 脚本是严格模式,有的 script 脚本是正常模式,这样不利于文件合并,所以可以将整个脚本文件放在一个立即执行的匿名函数之中。这样独立创建一个作用域而不影响其他script 脚本文件。
(function (){
//在当前的这个自调用函数中有开启严格模式,当前函数之外还是普通模式
"use strict";
var num = 10;
function fn() {
}
})();
//或者
<script>
"use strict"; //当前script标签开启了严格模式
</script>
<script>
//当前script标签未开启严格模式
</script>
- 函数开启严格模式
要给某个函数开启严格模式,需要把“use strict”; (或 ‘use strict’; ) 声明放在函数体所有语句之前。
function fn(){
"use strict";
return "123";
}
//当前fn函数开启了严格模式
- 严格模式产生的改变
'use strict'
num = 10
console.log(num)//严格模式后不能使用未声明的变量
------------------
var num2 = 1;
delete num2;//严格模式不允许删除变量
------------------
function fn() {
console.log(this); // 严格模式下全局作用域中函数中的 this 是 undefined
}
fn();
------------------
function Star() {
this.sex = '男';
}
// Star();严格模式下,如果构造函数不加new调用, this 指向的是undefined 如果给他赋值则会报错.
var ldh = new Star();
console.log(ldh.sex);
--------------------
setTimeout(function() {
console.log(this); //严格模式下,定时器 this 还是指向 window
}, 2000);
*预解析
- 预解析:在当前作用域下, JS 代码执行之前,浏览器会默认把带有 var 和 function 声明的变量在内存中进行提前声明或者定义。预解析也叫做变量、函数提升。
预解析会把变量和函数的声明在代码执行之前执行完成。 - 变量预解析
变量提升(变量预解析): 变量的声明会被提升到当前作用域的最上面,变量的赋值不会提升。
console.log(num); // 结果是多少?
var num = 10;
//结果:undefined,变量提升只提升声明,不提升赋值
- 函数预解析
函数提升: 函数的声明会被提升到当前作用域的最上面,但是不会调用函数。函数表达式不存在提升的现象
fn();
function fn() {
console.log('打印');
}
结果:控制台打印字符串 — ”打印“
注意:函数声明代表函数整体,所以函数提升后,函数名代表整个函数,但是函数并没有被调用!
fn();
var fn = function() {
console.log('想不到吧');
}
结果:报错提示 ”fn is not a function"
解释:该段代码执行之前,会做变量声明提升,fn在提升之后的值是undefined;而fn调用是在fn被赋值为函数体之前,此时fn的值是undefined,所以无法正确调用
对象 & 数组
- 查找文档:MDN
堆、栈
- 堆栈空间分配区别:
1、栈(操作系统):由操作系统自动分配释放存放函数的参数值、局部变量的值等。其操作方式类似于数据结构中的栈;简单数据类型存放到栈里面
2、堆(操作系统):存储复杂类型(对象),一般由程序员分配释放,若程序员不释放,由垃圾回收机制回收。 - 简单数据类型的存储方式
值类型变量的数据直接存放在变量(栈空间)中
- 复杂数据类型的存储方式
引用类型变量(栈空间)里存放的是地址,真正的对象实例存放在堆空间中
- 简单类型传参
函数的形参也可以看做是一个变量,当我们把一个值类型变量作为参数传给函数的形参时,其实是把变量在栈空间里的值复制了一份给形参,那么在方法内部对形参做任何修改,都不会影响到的外部变量。
function fn(a) {
a++;
console.log(a);
}
var x = 10;
fn(x);
console.log(x);
// 运行结果如下:
11
10
- 复杂数据类型传参
函数的形参也可以看做是一个变量,当我们把引用类型变量传给形参时,其实是把变量在栈空间里保存的堆地址复制给了形参,形参和实参其实保存的是同一个堆地址,所以操作的是同一个对象。
function Person(name) {
this.name = name;
}
function f1(x) {
// x = p
console.log(x.name); // 2. 这个输出什么 ?
x.name = "张学友";
console.log(x.name); // 3. 这个输出什么 ?
}
var p = new Person("刘德华");
console.log(p.name); // 1. 这个输出什么 ?
f1(p);
console.log(p.name); // 4. 这个输出什么 ?
new专题
数字安全专题 (0.1+0.2!==0.3 的原因)
点击跳转
总结:JS数字采用 IEEE 754 标准 64 位双精度浮点数,1-11-52,用科学记数法表示指数和尾数,过长小数会被截断,导致精度误差。解决方法:高精度数值库Decimal.js;在ES6中,提供了Number.EPSILON属性,而它的值就是 2 − 52 2^{-52} 2−52
function numberepsilon(arg1,arg2){
return Math.abs(arg1 - arg2) < Number.EPSILON;
}
console.log(numberepsilon(0.1 + 0.2, 0.3)); // true
包装对象(Wrapper Objects)
- JavaScript 不会把所有基本数据类型“始终”包装为复杂数据类型。只会在需要对基本数据类型进行“对象式操作”(即调用其方法或访问其属性)时,临时地将它们包装成对应的对象,操作完成后立即销毁这个临时对象。
JavaScript 引擎在后台自动进行以下步骤:
1)