JavaScript
操作文档API
文档对象模型(DOM)
选择DOM元素: document.querySelector(‘a’)
(较老的浏览器中会使用document.getElementById()等方法)
创建新结点:document.createElement(‘p’)
(创建文本节点 :document.createTextNode())
插入DOM中:document.appendChild()
(insertBefore() 方法:可在已有的子节点前插入一个新的子节点。语法 :insertBefore(newchild,refchild))
删除结点:removeChild()
(删除自身结点:Node.parentNode.removeChild(Node))
操作样式
Node.style.color = ‘white’
window API
window.innerWidth 和 window.innerHeight分别可以获取窗口宽高。
js中var const let的区别
ES6中一般用let来代替var,用const来定义常量。
XMLHttpRequest(XHR)
步骤:1.创建一个新的请求对象:let request = new XMLHttpRequest();
2.使用open()方法指定HTTP请求的方法和url:request.open(‘GET’, url);
3.设置期待的响应类型(默认为Text):request.responseType = ‘text’;
4.send()函数发送请求:request.send();
Fetch
fetch(url)相当于request.open()
fetch返回一个promise使用then()函数进行处理,相当于XHR中的onload()事件处理程序。
使用.text()方法是将响应作为原始文本返回,相当于responseType = ‘Text’
2020/7/27补充:fetch发送post请求为什么总发送两次,第一次状态码是204,第二次才成功?
因为使用fetch请求时,导致fetch第一次发送了一个options请求,询问服务器是否支持,如果支持才在第二次中发送真正的请求。
关于promise
fetch()会返回一个解析HTTP响应的promise,在.then()中定义的任何函数会被自动给予一个响应作为参数,意思是无论参数名称可以是任何名字,实体都是返回的响应。
XHR和Fetch如何选择
XHR面世较久,比较成熟,老式的浏览器都支持,Fetch和promise都是比较新的产物,不过大部分浏览器都已经支持。
客户端存储API
Web Storage API
- localStorage
为每一个给定的源(相同域名的URL)维持一个独立的存储区域。即使关闭浏览器数据依然存在,除非手动删除。 - sessionStorage
功能与localstorage相同,但是生命周期不同。当关闭浏览器后自动删除,只要浏览器处于打开状态,页面刷新和恢复该数据依然存在。
检测localStorage是否可用函数
function storageAvailable(type) {
var storage;
try {
storage = window[type];
var x = '__storage_test__';
storage.setItem(x, x);
storage.removeItem(x);
return true;
}
catch(e) {
return e instanceof DOMException && (
// everything except Firefox
e.code === 22 ||
// Firefox
e.code === 1014 ||
// test name field too, because code might not be present
// everything except Firefox
e.name === 'QuotaExceededError' ||
// Firefox
e.name === 'NS_ERROR_DOM_QUOTA_REACHED') &&
// acknowledge QuotaExceededError only if there's something already stored
(storage && storage.length !== 0);
}
}
设置值:localStorage.setItem();
获取值:localStorage.getItem();
删除记录:localStorage.removeItem();
localStorage.clear(); 清空域名对应的整个存储对象
额外知识:事件监听器在对象发生改变时会触发,即创建/更新/删除数据时,重复设置相同的键值不会触发。
IndexedDB API
IndexedDB是一个事务型数据库系统,类似于基于SQL的RDBMS。用于储存大量的结构化数据,而localStorage用于存储少量数据。
Web Worker API
使用Web Workers可以在后台独立运行一个脚本操作,从而使主线程不会因此阻塞。
使用构造函数(如Woker()创建一个worker对象,构造函数接受一个JavaScript文件URL。可以在worker线程中运行任意代码,除了直接操纵DOM元素以及一些window对象中的某些方法和属性。
主线程和worker之间使用postMessage()
方法来发送消息。并通过onmessage这个event handler来接受消息。
第三方API
JavaScript语法
在函数中有一个名为arguments
的内部对象,包括了被传入的所有参数:
function add() {
var sum = 0;
for (var i = 0, j = arguments.length; i < j; i++) {
sum += arguments[i];
}
return sum;
}
add(2, 3, 4, 5); // 14
也可以这样表示:
function avg(...args) {
var sum = 0;
for (let value of args) {
sum += value;
}
return sum / args.length;
}
avg(2, 3, 4, 5); // 3.5
需要注意的是,无论“剩余参数操作符”被放置到函数声明的哪里,它都会把除了自己之前的所有参数存储起来。比如函数:function avg(firstValue, …args) 会把传入函数的第一个值存入 firstValue,其他的参数存入 args。
闭包!!(划重点)
现在先看这段代码
function makeAdder(a) {
return function(b) {
return a + b;
}
}
var add5 = makeAdder(5);
var add20 = makeAdder(20);
add5(6); // ?
add20(7); // ?
一开始看的我一脸懵逼,完全无法理解add5和add20明明是两个函数的引用为什么还能传入参数?
看官方的解释:
当调用 makeAdder 时,解释器创建了一个作用域对象,它带有一个属性:a,这个属性被当作参数传入 makeAdder 函数。然后 makeAdder 返回一个新创建的函数(暂记为 adder)。通常,JavaScript 的垃圾回收器会在这时回收 makeAdder 创建的作用域对象(暂记为 b),但是,makeAdder 的返回值,新函数 adder,拥有一个指向作用域对象 b 的引用。最终,作用域对象 b 不会被垃圾回收器回收,直到没有任何引用指向新函数 adder。
其实已经很明白了,makeAdder返回值为一个函数,add5和add20分别指向各自函数的引用,所以属性a仍然被引用所以不会被回收。
闭包概念:一个闭包,就是 一个函数 与其 被创建时所带有的作用域对象 的组合。(我的理解就是makeAdder就是一个闭包)
看到一个更详细的解释,上链接:
https://www.cnblogs.com/frankfang/archive/2011/08/03/2125663.html
重要信息:
- 当你return的是内部function时,就是一个闭包。内部function会close-over外部function的变量直到内部function结束。
- 如果一个函数访问了它的外部变量,那么它就是一个闭包。从技术上来讲,在JS中,每个function都是闭包,因为它总是能访问在它外部定义的数据。
- 闭包经常用于创建含有隐藏数据的函数(但并不总是这样)
很受用的例子:
var db = (function() {
// 创建一个隐藏的object, 这个object持有一些数据
// 从外部是不能访问这个object的
var data = {};
// 创建一个函数, 这个函数提供一些访问data的数据的方法
return function(key, val) {
if (val === undefined) { return data[key] } // get
else { return data[key] = val } // set
}
// 我们可以调用这个匿名方法
// 返回这个内部函数,它是一个闭包
})();
db('x'); // 返回 undefined
db('x', 1); // 设置data['x']为1
db('x'); // 返回 1
// 我们不可能访问data这个object本身
// 但是我们可以设置它的成员
闭包这个概念真的很难理解,看了这么多解释也是稀里糊涂。
https://stackoverflow.com/questions/111102/how-do-javascript-closures-work
MDN和上面这位大神都推荐了stackoverflow的这篇文章,有机会一定要去看看。
算法题
用两个栈实现队列
用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )
示例 1:
输入:
[“CQueue”,“appendTail”,“deleteHead”,“deleteHead”]
[[],[3],[],[]]
输出:[null,null,3,-1]
示例 2:
输入:
[“CQueue”,“deleteHead”,“appendTail”,“appendTail”,“deleteHead”,“deleteHead”]
[[],[],[5],[2],[],[]]
输出:[null,-1,null,null,5,2]
class CQueue {
Stack<Integer> stack1 = new Stack<Integer>();//存放临时数据
Stack<Integer> stack2 = new Stack<Integer>();//正向存放数据
public CQueue() {
}
public void appendTail(int value) {
while(!stack2.empty()){
stack1.push(stack2.pop());
}
stack2.push(value);
while(!stack1.empty()){
stack2.push(stack1.pop());
}
}
public int deleteHead() {
if(stack2.empty()){
return -1;
}else{
return stack2.pop();
}
}
}
我的思路是一个栈只有在存入新数据时用于存放临时变量,另一个栈用于存放队列中的数据。这样删除头元素只需要pop存放数据的栈的栈顶元素即可。
官方题解有一个回答是一个栈用于插入操作,一个栈用于删除操作,大致思路差不多。题目比较简单。
斐波那契数列
写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项。斐波那契数列的定义如下:
F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
class Solution {
public int fib(int n) {
if(n == 0) return 0;
if(n == 1) return 1;
int[] dp = new int[n+1];
//int dp1 = 0;
//int dp2 = 1;
int res = 0;
for(int i = 2; i < n+1 ; i++){
dp[i] = dp[i-1] + dp[i-2];
dp[i] %= 1000000007;
}
return dp[i];
}
}
本题虽然是简单难度,但是第一想法是用递归做,但是当n>=45时递归的方法就会超时(也是被这题的标签误导了),然后一看大神的题解是用动态规划做,这样时间和空间复杂度都是O(n)。还有空间复杂度更小的做法,其实使用两个变量来储存dp[i-1]和dp[i-2]就可以了,这样空间复杂度只有O(1)。