变量类型和计算
typeof能判断哪些类型
- 能识别六种值类型(undefined, string, number, boolean, symbol, bigint)
- 能判断 function 类型
- 不能判断三种引用类型(object, array, null)
值类型和引用类型的区别
- 值类型直接存在栈
- 引用类型存放在堆,地址存在栈
在物理结构上,堆栈存放在一块区域,栈从前往后放置,堆从后往前放置
【……栈栈->……………………<-堆堆……】
计算问题
- 加法运算
const a = 100 + 10 // 110
const b = 100 + '10' // '10010'
const c = true + '10' // 'true10'
- == 运算
100 == '100' // true
0 == '' // true
0 == false // true
false == '' // true
null == undefined // true
/**
除了 == null 之外,其他一律用 ===
因为: obj.a == null,
相当于:obj.a === null || obj.a === undefined
*/
- 逻辑运算
// truly和falsely
!!0 === false
!!NaN === false
!!'' === false
!!null === false
!!undefined === false
!!false === false
// 其余全为true
// 在if(exp)内部的表达式就是truly和falsely
手写深拷贝
function deepClone(obj) {
if(typeof obj !== 'object' || obj == null){
return obj;
}
let res;
if(obj instanceof Array){
res = [];
}else{
res = {};
}
for(let key in obj){
if(obj.hasOwnProperty(key)){
res[key] = deepClone(obj[key]);
}
}
return res;
}
原型和原型链
instanceof
- xxx instanceof Array
- xxx 这个值或者对象 是否继承自 Array
- Array 是否存在于 xxx 的原型链上
继承
class People{
constructor(name){
this.name = name;
}
}
class Student extends People{
constructor(name, number){
super(name);
this.number = number;
}
}
原型 & 原型链
原型
- 每个class都有显式原型prototype
- 每个实例对象都有隐式原型__proto__
- 实例对象的__proto__指向 对应 class 的 prototype
原型链
手写一个jQuery,考虑插件和扩展性
class jQuery{
constructor(selector){
const result = document.querySelectorAll(selector);
const length = result.length;
for(let i = 0; i < length; i++){
this[i] = result[i];
}
this.length = length;
this.selector = selector;
}
get(index){
return this[index];
}
each(fn){
for(let i = 0; i < this.length; i++){
const elem = this[i];
fn(elem);
}
}
on(type, fn){
return this.each(elem => {
elem.addEventListener(type,fn,false);
});
}
// 可以扩展很多DOM API
}
// 添加插件
jQuery.prototype.dialog = function(info){
alert(info);
}
// 复写,也就是'造轮子'
class myJQuery extends jQuery{
constructor(selector){
super(selector);
}
// 扩展自己的方法
}
// 使用
const $p = new jQuery('p');
$p.get(1);
$p.each((elem) => console.log(elem.nodeName));
$p.on('click', () => alert('clicked'))
作用域和闭包
作用域 & 自由变量
作用域
- 全局作用域
- 函数作用域
- 块级作用域(ES6新增)
自由变量
- 一个变量在当前作用域没有定义,但被使用了
- 向上级作用域,一层一层依次寻找,知道找到为止
- 如果到全局作用域都没找到,则报错 xx id not defined
闭包及应用场景
闭包是作用域应用的特殊情况,有两种表现:
- 函数作为参数被传递
- 函数作为返回值被返回
自由变量的查找规则:
所有的自由变量的查找,是在函数定义的地方,向上级作用域查找,不是在执行的地方!!!
function print(fn){
let a = 200;
fn();
}
let a = 100;
function fn(){
console.log(a);
}
print(fn); // 100
应用场景:
- 用闭包隐藏数据,只暴露API
function createCache() {
const data = {};
return {
set: function(key, val) {
data[key] = val;
},
get: function(key) {
return data[key]
}
}
}
const c = createCache();
c.set('a',100);
console.log(c.get('a'));
this在不同应用场景下,如何取值
- 作为普通函数
- 使用call,apply,bind
- 作为对象方法被调用
- 在class方法中调用
- 箭头函数
- 原型链上的this
原型链的this:xialuo.__proto__.sayHi.call(xialuo)
this 取的值是在执行的时候确定的,不是在定义的时候确定的。
手写bind函数
Function.prototype.myBind = function () {
const args = Array.prototype.slice.call(arguments);
const t = args.shift();
const self = this;
return function(){
return self.apply(t,args);
}
}
异步
JS是单线程的,只能同时做一件事
浏览器和nodejs已支持JS启动进程,如 Web Worker
JS和DOM渲染共用一个线程,因为JS可修改DOM结构
遇到等待(发送请求,定时任务)不能卡住,所以需要异步,通过回调实现
同步和异步的区别是什么
- 基于JS是单线程语言
- 异步不会阻塞代码执行
- 同步会阻塞代码执行
应用场景
- 网络请求,如 ajax 图片加载
- 定时任务,如 setTimeout
Promise 实现异步图片加载
function loadImg(src) {
const p = new Promise(
(resolve, reject) => {
let img = document.createElement('img');
img.onload = () => {
resolve(img);
}
img.onerror = () =>{
reject(new Error(`图片加载失败 ${src}`))
}
img.src = src;
}
)
return p;
}
const url1 = '/xxx.png';
const url2 = '/yyy.png';
loadImg(url1).then(img1 => {
console.log(img1.width)
return loadImg(url2);
}).then(img2 =>{
console.log(img2.width);
}).catch(err => console.log(err))