30.typescript与js的比较
js主要特点:
JavaScript 是一种脚本编写语言,无需编译,只要嵌入 HTML 代码中,就能由浏览器逐行加载解释执行。
JavaScript 是一种基于对象的语言,可以创建对象同时使用现有对象。但是 Javascript 并不支持其它面向对象语言所具有的继承和重载功能。
JavaScript 的语法简单,使用的变量为弱类型。
JavaScript 语言较为安全,仅在浏览器端执行,不会访问本地硬盘数据。
JavaScript 语言具有动态性。JavaScript 是事件驱动的,只根据用户的操作做出相应的反应处理。
JavaScript 只依赖于浏览器,与操作系统的因素无关。因此 JavaScript 是一种跨平台的语言。
JavaScript 兼容性较好,能够与其他技术(如 XML,REST API 等)一起使用。
ts主要特点:
TypeScript 是 Microsoft 推出的开源语言,使用 Apache 授权协议
TypeScript 增加了静态类型、类、模块、接口和类型注解
TypeScript 可用于开发大型的应用
TypeScript 易学易于理解
js和ts的主要差异:
TypeScript 从核心语言方面和类概念的模塑方面对 JavaScript 对象模型进行扩展。
JavaScript 代码可以在无需任何修改的情况下与 TypeScript 一同工作,同时可以使用编译器将 TypeScript 代码转换为 JavaScript。
TypeScript 通过类型注解提供编译时的静态类型检查。
TypeScript 中的数据要求带有明确的类型,JavaScript不要求。
TypeScript 为函数提供了缺省参数值。
TypeScript 引入了 JavaScript 中没有的“类”概念。
TypeScript 中引入了模块的概念,可以把声明、数据、函数和类封装在模块中。
31.new操作符的流程(构造函数实现原理)
1、创建一个空对象
var obj = new Object();
2、设置原型链(当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象)
obj.__proto__= Func.prototype;
3、让Func中的this指向obj,并执行Func的函数体。(创建新的对象之后,将构造函数的作用域赋给新对象(因此this就指向了这个新对象))
var result =Func.call(obj);
32.for in 和for of
1.for in 遍历数组会遍历到数组原型上的属性方法,更适合遍历对象;
2.forEach 不支持break,continue,return;
3.for of可以成功遍历数组的值,而不是索引,不会遍历原型;
4.for in 可以遍历到myObject的原型方法method,如果不想遍历原型方法和属性的话,可以在循环内部判断一下,hasOwnPropery方法可以判断某属性是否是该对象的实例属性
33.JS实现并发控制
递归方式实现:
//递归方式
function handleFetchQueue(urls, max, callback) {
let urlsCopy = [...urls]; //防止影响外部urls变量
let count = 0; //几率并发数
request();
function request() {
count++;
console.log('start 当前并发数为: ' + count);
//请求
fetch(urlsCopy.shift()).then(Handle).catch(Handle);
//并发数不足时递归
count < max && request();
function Handle() {
count--;
console.log('end 当前并发数为: ' + count);
if (urlsCopy.length) { //还有未请求的则递归
request();
} else if (count === 0) { //并发数为0则表示全部请求完成
callback()
}
}
}
}
promise方式实现:
function sendResquest(urls, max, callback) {
let pending_count = 0, //并发数
idx = 0; //当前请求的位置
while (pending_count < max) {
_fetch(urls[idx++])
}
async function _fetch(url) {
if (!url) return;
pending_count++;
console.log(url + ':start', '并发数: ' + pending_count);
await fetch(url)
pending_count--;
console.log(url + ':done', '并发数: ' + pending_count);
_fetch(urls[idx++]);
pending_count || callback && callback()
}
}
验证:
let urls = Array.from({
length: 10
}, (v, k) => k);//[0,1,2,3,4,5,6,7,8,9];模拟url序列
let fetch = function (idx) {//模拟fetch
return new Promise(resolve => {
let timeout = parseInt(Math.random() * 1e4);
setTimeout(() => {
resolve(idx)
}, timeout)
})
};
let max = 4;//最大并发数
let callback = () => {//回调函数
console.log('run callback');
};
//执行
handleFetchQueue(urls, max, callback);//分别调用 ;
sendResquest(urls, max, callback);
34.ajax、axios、fetch比较
原生ajax:
1.使用起来也比较繁琐,需要设置很多值。
2.早期的IE浏览器有自己的实现,这样需要写兼容代码
3.如果有多个请求,并且有依赖关系的话,容易形成回调地狱。
4.本身是针对MVC的编程,不符合现在前端MVVM的浪潮。
fetch:
fetch
只对网络请求报错,对400
,500
都当做成功的请求,需要封装去处理fetch
默认不会带cookie
,需要添加配置项。fetch
不支持abort
,不支持超时控制,使用setTimeout
及Promise.reject
的实现超时控制并不能阻止请求过程继续在后台运行,造成了流量的浪费。fetch
没有办法原生监测请求的进度,而XHR
可以。
axios:
从浏览器中创建
XMLHttpRequests
从
node.js
创建http
请求支持
Promise
API拦截请求和响应
转换请求数据和响应数据
取消请求
自动转换
JSON
数据客户端支持防御
XSRF
总结:
ajax是最早出现发送后端请求的技术,属于原生js范畴,核心是使用XMLHttpRequest对象,使用较多并有先后顺序的话,容易产生回调地狱。
fetch号称可以代替ajax的技术,是基于es6中的Promise对象设计的,参数和jQuery中的ajax类似,它并不是对ajax进一步封装,它属于原生js范畴。没有使用XMLHttpRequest对象。
axios不是原生js,使用时需要对其进行安装,客户端和服务器端都可以使用,可以在请求和相应阶段进行拦截,基于promise对象。
35.promise.race、promise.all实现
先写个方法,用来判断是不是promise:
function isPromise(obj) {
return obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function';
}
promise.all:
Promise.all = function (promiseArr) { //传入promise数组
if (!Array.isArray(promiseArr)) {
throw new TypeError('You must pass array')
}
for (let i of promiseArr) {
if (!isPromise(i)) {
throw new TypeError('not Promise')
}
}
let results = []; //用来收集各个promise的结果
let promiseCount = 0; //标记是否都执行 ?== promisesLength
let promisesLength = promiseArr.length;
return new Promise(function (resolve, reject) {
for (let val of promiseArr) { //遍历
Promise.resolve(val).then(function (res) {
results[promiseCount] = res; //存入结果
promiseCount++; //标志加一
// 当所有函数都正确执行了,resolve输出所有返回结果。
if (promiseCount === promisesLength) {
return resolve(results);
}
}, function (err) { //有错误直接抛出
return reject(err);
});
}
});
};
promise.race:
Promise.race = function(promiseArr){
if (!Array.isArray(promiseArr)) {
throw new TypeError('You must pass array')
}
for (let i of promiseArr) {
if (!isPromise(i)) {
throw new TypeError('not Promise')
}
}
return new Promise(function(resolve,reject){
for(let i = 0;i<promiseArr.length;i++){
promiseArr[i].then(resolver,rejecter);
}
function resolver(res){
resolve(res);
}
function rejecter(error){
reject(error);
}
})
}
测试:
let p1 = Promise.resolve('hello I am No1');
let p2 = Promise.resolve('hello I am No1');
let p3 = Promise.resolve('hello I am No3');
Promise.all([p2,p1,p3]).then((value) => {
console.log(value);
});
//输出:hello I am No2
// hello I am No1
// hello I am No3
Promise.race([p2,p1,p3]).then((value) => {
console.log(value)
});
//输出:hello I am No2
36.symbol实现原理
ES6引入了一-种新的原始数据类型Symbol,表示独一无二的值。
主要特性:
Symbol值通过Symbol函数生成,使用typeof,结果为"symbol"
var s = Symbol();
console. log(typeof s); // "symbol"
Symbol函数前不能使用new命令,否则会报错。这是因为生成的Symbol是一个原始类型的值,不 是对象。
instanceof的结果为false
var S = Symbol('foo');
console.1og(s instanceof Symbol); // false
Symbol函数可以接受一个字符串作为参数,表示对Symbol实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分。
var s1 = Symbol('foo');
console.1og(s1); // Symbol(foo)
5.如果Symbol的参数是一个对象,就会调用该对象的toString方法,将其转为字符串,然后才生成一个Symbol值。
const obj = {
toString() {
return ' abc' ;
}
};
const sym = Symbol(obj);
console.1og(sym); // Symbol(abc)
6.Symbol函数的参数只是表示对当前Symbol值的描述,相同参数的Symbol函数的返回值是不相等的。
//没有参数的情况
var s1 = Symbol();
var s2 = Symbol();
console.1og(s1 === s2); // false
//有参数的情况
var s1 = Symbol('foo' );
var s2 = Symbol('foo');
console.1og(s1 === s2); // false
7.Symbol值不能与其他类型的值进行运算,会报错。
var sym = Symbol('My symbol');
console. log("your symbol is " + sym); // TypeError: can't convert symboL to string
8.Symbol值可以显式转为字符串。
var sym = Symbol('My symbol');
console. log(String(sym)); // 'Symbol (My symbol) '
console. log( sym. tostring()); // 'Symbol (My symbol) '
9.Symbol值可以作为标识符,用于对象的属性名,可以保证不会出现同名的属性。
var mySymbol = Symbol();
//第一种写法
vara={};
a[mySymbol] = 'Hello!';
//第二种写法
vara = {
[mySymbol]: 'Hello!
};
//第三种写法
vara={};
object . defineProperty(a, mySymbol, { value: 'Hello!' });
//以上写法都得到同样结果
console. log(a[mySymbol]); // "Hello!"
10.Symbol作为属性名,该属性不会出现在fr..in、fr..of 循环中,也不会被Object.keys()、 Object.getOwnPropertyNames()、JSON.stringify() 返回。但是,它也不是私有属性,有一个 Object.getOwnPropertySymbols方法,可以获取指定对象的所有Symbol属性名。
var obj = {};
var a = Symbol('a');
var b = Symbol('b');
obj[a] = 'Hello';
obj[b] = 'World';
var objectsymbols = object . getOwnPropertysymbols(obj);
console. log(objectsymbols);
// [Symbol(a),Symbol(b)]
11.如果我们希望使用同一个Symbol值,可以使用Symbol.for。它接受一个字符串作为参数,然后搜索有没有以该参数作为名称的Symbol值。如果有,就返回这个Symbol值,否则就新建并返回一个以该字符串为名称的Symbol值。
var s1 = Symbol.for('foo' );
var s2 = Symbol.for('foo');
console.log(s1 === s2); // true
12.Symbol.keyFor方法返回-个已登记的Symbol类型值的key。
var s1 = Symbol.for("foo");
console.log(Symbol.keyFor(s1)); // "foo"
var s2 = Symbol("foo");
console.log(Symbol. keyFor(s2) ); // undefined
37.ES6的装饰器
装饰器是一种函数,写成@ + 函数名。它可以放在类和类方法的定义前面。
许多面向对象的语言都有修饰器(Decorator)函数,用来修改类的行为。
例1:
@testable
class MyClass {};
function testable(target) {
target.isTestable = true;
}
MyClass.isTestable; // true
上面代码中,@testable就是一个修饰器。它修改了MyClass这个类的行为,为它加上了静态属性isTestable。
例2:
@decorator
class A {};
// 等同于
class A {};
A = decorator(A) || A;
也就是说,修饰器是一个对类进行处理的函数。修饰器函数的第一个参数,就是所要修饰的目标类。
注意,修饰器对类的行为的改变,是代码编译时发生的,而不是在运行时。这意味着,修饰器能在编译阶段运行代码。也就是说,修饰器本质就是编译时执行的函数。
如果觉得一个参数不够用,可以在修饰器外面再封装一层函数。见下面
例3:
function testDecorator(desc) {
return function(target) {
target.addedParam = desc;
}
}
//调用testDecorator函数之后,其又返回一个匿名函数,利用闭包的特性使用了decs变量
@testDecorator("operate one")
class ClassA {
}
console.log(ClassA.addedParam) // operate one
38.实现fetch abort(中止请求)
// 创建 AbortController 的实例
const controller = new AbortController()
const signal = controller.signal
// API 10s 后返回相应
// https://slowmo.glitch.me/10000 10000 代表 10s 后返回相应值
//fetch 接受Signal 作为第二个参数的一部分。
fetch('https://slowmo.glitch.me/10000', {
signal
})
.then(r => r.json())
.then(response => console.log(response))
.catch(err => {
if (err.name === 'AbortError') {
console.log('Fetch was aborted')
} else {
console.log('Error', err)
}
})
// 在 5s 后中断请求,将触发 'AbortError'
setTimeout(() => controller.abort(), 5000);
40.使用xhr实现fetch
// 先实现ajax
function ajax(method,url,data,suc,fail) {
if (window.XMLHttpRequest)
{
// IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码
xhr=new XMLHttpRequest();
}
else
{
// IE6, IE5 浏览器执行代码
xhr=new ActiveXObject("Microsoft.XMLHTTP");
}
xhr.open(method, url, true);
xhr.onreadystatechange = function () {
if(xhr.readyState == 4){
if(xhr.status == 200){
suc(xhr.responseText)
} else {
console.log(err);
fail(xhr.responseText);
}
}
};
xhr.send(data);
}
// 再实现promise
function promise () {
this.msg = '' // 存放value和error
this.status = 'pending'
var that = this
var process = arguments[0]
process (function () {
that.status = 'fulfilled'
that.msg = arguments[0]
}, function () {
that.status = 'rejected'
that.msg = arguments[0]
})
return this
}
promise.prototype.then = function () {
if (this.status === 'fulfilled') {
arguments[0](this.msg)
} else if (this.status === 'rejected' && arguments[1]) {
arguments[1](this.msg)
}
}
//fetch
function fetch(method, url, data) {
return new promise(function (resolve,reject) {
ajax(method, url, data, function (res) {
resolve(res);
},function (err) {
reject(err);
})
})
}
41.js基本数据类型与引用数据类型的存储
JS 的数据类型有几种:
Number、String、Boolean、Null、undefined、object、symbol
Object 中包含了哪几种类型:
Date、function、Array等
JS的基本类型和引用类型有哪些呢:
基本类型:String、Number、boolean、null、undefined、symbol
引用数据类型:object
jS 中 typeof 输出分别是什么:
null 和 undefined 有什么区别:
1.Null 只有一个值,是 null。不存在的对象。
2.Undefined 只有一个值,是undefined。没有初始化。
3.undefined 是从 null 中派生出来的。
简单理解就是:undefined 是没有定义的,null 是定义了但是为空
== 和 === 有什么区别,什么场景下使用:
1、对于string,number等基础类型,==和===是有区别的
不同类型间比较,==之比较“转化成同一类型后的值”看“值”是否相等,===如果类型不同,其结果就是不等
同类型比较,直接进行“值”比较,两者结果一样
2、对于Array,Object等高级类型,==和===是没有区别的
3、基础类型与高级类型,==和===是有区别的
对于==,将高级转化为基础类型,进行“值”比较
因为类型不同,===结果为false
42.this指向
this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,实际上this的最终指向的是那个调用它的对象;
另外,call,apply,bind可以改变this的指向(实现见 题8);
43.图片懒加载和预加载的实现
懒加载:
html:
src="./images/birthday_flower(20).jpg" src="./images/love_flower(1).png" alt="">
src="./images/birthday_flower(20).jpg" src="./images/love_flower(3).jpg" alt="">
<script>
var num = document.getElementsByTagName('img').length;
var img = document.getElementsByTagName("img");
// 存储图片加载到的位置,避免每次都从第一张图片开始遍历
var n = 0;
// 页面载入完毕加载可视区域内的图片
window.addEventListener("scroll",function lazyload() {
// 可见区域高度
var seeHeight = document.documentElement.clientHeight;
console.log('seeHeight: ', seeHeight);
// 滚动条距离顶部高度
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
console.log('scrollTop: ', scrollTop);
for (var i = n; i < num; i++) {
if (img[i].offsetTop < seeHeight + scrollTop) {
if (img[i].getAttribute("src") == "./images/birthday_flower(20).jpg") {
img[i].src = img[i].getAttribute("src");
}
n = i + 1;
}
}
},false);
// 监听页面滚动事件
script>
预加载:
纯css:
原理是加载了该图片,但是我们不显示在可视范围内:
body:after {
content: "";
display: block;
position: absolute;
background: url("../image/manage/help/help_item2_01.png?v=201707241359") no-repeat -10000px -1000px;
width: 0;
height: 0
}
纯js:
//存放图片路径的数组
var imgSrcArr = [
'imgsrc1',
'imgsrc2',
'imgsrc3',
'imgsrc4'
];
var imgWrap = [];
function preloadImg(arr) {
for(var i =0; i< arr.length ;i++) {
imgWrap[i] = new Image();
imgWrap[i].src = arr[i];
}
}
preloadImg(imgSrcArr);
//或者延迟的文档加载完毕在加载图片
js+css:
比如我们写了上面的这样一个类,但是页面中没有用到,我们在文档加载完毕之后,给某个元素添加该类
.preload-img:after{
content:"",
background: url("../image/manage/help/help_item2_01.png?v=201707241359") no-repeat -10000px -1000px,
}
var target = document.getElementById(".target");
target.setAttribute("class") = "preload-img";