变量类型
- 值类型和引用类型
- 值类型只能存储一个值
- 值类型复制就是值类型本身
- 引用类型复制只是复制引用类型的指针
- 引用类型:对象,数组,函数
- 引用类型特点:可以无限制的扩展属性
- JS中使用typeof能得到哪些类型:7种类型
- undefined string number boolean symbol object function
- typeof不能详细区分引用类型(对象、数组)的详细类型
- 但是可以详细区分function以及所有值类型。
typeof a//undefined
typeof undefined//undefined
typeof 'abc' //string
typeof 123 //number
typeof NaN //number
typeof true //boolean
typeof {} //object
typeof [] //object
typeof null //object
typeof console.log //function
typeof Symbol('foo') //symbol
变量计算-强制类型转换
- 字符串拼接
let a=100;
let b=10;
console.log(a+b);//110
let a=100;
let b='10';
console.log(a+b);//10010
console.log(a-b);//90
- ==运算符
100=='100'//true
0==''//true(0和''都可以转换成false)
null==undefined//true(null和undefined都可以转换成false)
- if语句
let a=true
if(a){
console.log(a);//true
}
let b=100;
if(b){
console.log(b);//100
}
let c='';
if(c){
console.log('c')
}
//c=''是false,if语句不执行
- 逻辑运算
console.log(10&&0);//0
console.log(''||'abc');//abc
console.log(!window.abc);//true
//判断一个变量会被当做true还是false
var a=100;
console.log(!!a);//true
- 何时使用===,何时使用 ==
//仅有这种情况使用'=='
if(obj.a==null){
//此时条件相当于obj.a===null||obj.a===undefined,简写形式
//这是jQuery源码中推荐的写法
}
除此之外,其它情况均建议使用'==='
- JS有哪些内置函数
- js内置函数是浏览器内核自带的,不用任何函数库引入就可以直接使用的函数。
- Object,Array,Boolean,Number,String,Function,Date,RegExp,Error
- Math是内置对象
- Math内置对象常用方法
- Math.floor():向下取整
- Math.ceil():向上取整
- Math.round():四舍五入
- Math.max(a,b):取a,b之间最大值
- Math.min(a,b):取a,b之间最小值
- Math.random:取0-1之前的随机数
let a=4.3;
console.log(Math.round(a));
console.log(Math.ceil(a));
console.log(Math.floor(a));
console.log(Math.max(5,3));
console.log(Math.min(1,4));
function random(min,max) {
return min+Math.random()*(max-min)
}
console.log(random(1,10));
- JS变量按照存储方式分为哪些类型,并描述其特点
- 值类型和引用类
- 值类型可以将数据分块存储在内存中
- 引用类型是多个变量共用一个内存块,引用类型的赋值是指定了一个指针,并不是真正的值的拷贝,它们之间是会相互干预的。
- 如何理解JSON
- JSON是JS中的一个内置对象
JSON.stringify({a:10,b:20}) //"{"a":10,"b":20}"将对象转换为字符串
JSON.parse('{"a":10,"b":20}') //{a: 10, b: 20}把字符串转换为对象
原型和原型链
- 构造函数
function Foo(name,age) {
this.name=name;
this.age=age;
this.class='class-1';
// return this;//默认有这一行
}
var f=new Foo('wbq',22);
- 构造函数-扩展
var a={};//其实是var a=new Object()的语法糖
var b=[];//其实是var b=new Array()的语法糖
function Foo() {};//其实是var Foo=new Function(){}的语法糖
- 原型规则和示例
- 所有的引用类型(数组、对象、函数),都具有对象特性,即可自由扩展属性(除了'null')
- 所有的引用类型(数组、对象、函数),都具有proto属性,属性值是一个普通的对象
- 所有的函数,都有一个prototype属性,属性值也是一个普通对象
- 所有的引用类型(数组、对象、函数),proto属性值指向它的构造函数的prototype属性
- 当试图得到一个对象的某个属性时,如果对象本身没有这个属性,那么会去它的proto(即它的构造函数的prototype)中寻找
var obj={};obj.a=100;
var arr=[];arr.a=100;
function fn(){};fn.a=100
console.log(obj.__proto__);//{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
console.log(arr.__proto__);//[constructor: ƒ, concat: ƒ, copyWithin: ƒ, fill: ƒ, find: ƒ, …]
console.log(fn.__proto__);//ƒ () { [native code] }
console.log(fn.prototype);//{constructor: ƒ}
console.log(obj.__proto__===Object.prototype);//true
function Foo(name,age) {
this.name=name;
this.age=age;
}
Foo.prototype.alertName=function () {
alert(this.name)
}
var f=new Foo('wbq');
f.printName=function () {
console.log(this.name)
}
f.printName();
f.alertName();
- 循环对象自身的属性方法
- for...in循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性)
function Foo(name,age) {
this.name=name;
this.age=age;
}
Foo.prototype.alertName=function () {
alert(this.name)
}
var f=new Foo('wbq');
f.printName=function () {
console.log(this.name)
}
f.printName();
f.alertName();
for(let i in f){
console.log(i);//name,age,printName,alertName
}
//如何只遍历对象自身的属性,不遍历继承的可枚举属性
for(let i in f){
if(f.hasOwnProperty(i)){
console.log(i,f[i])
}
}
- Object.keys(obj)
-
Object.keys返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)的键名。
-
大多数时候,我们只关心对象自身的属性。所以,尽量不要用for...in循环,而用Object.keys()代替。
-
Object.values(obj):方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值。
-
bject.entries():方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组。
console.log(Object.keys(f));// ["name", "age", "printName"]
let obj={
'name':'wbq',
'age':20,
sayName(){
console.log(this.name)
}
}
console.log(Object.keys(obj));//[ 'name', 'age', 'sayName' ]
console.log(Object.values(obj));//[ 'wbq', 20, [Function: sayName] ]
console.log(Object.entries(obj));
// [ [ 'name', 'wbq' ],
// [ 'age', 20 ],
// [ 'sayName', [Function: sayName] ] ]
- Object.getOwnPropertyNames(obj)
Object.getOwnPropertyNames返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)的键名。
- Object.getOwnPropertySymbols(obj)
Object.getOwnPropertySymbols返回一个数组,包含对象自身的所有 Symbol 属性的键名。
- Reflect.ownKeys(obj)
Reflect.ownKeys返回一个数组,包含对象自身的所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举。
- 原型链
如果对象本身没有某个属性,则通过proto向上查找,最终都没有这个属性,则返回null,这种通过proto一级一级向上查找,则形成了原型链
- instanceof
- 用于判断引用类型属性是哪个构造函数的方法
- f instanceof Foo的判断逻辑是:
- f的proto一层一层往上,能否对应到Foo.prototype
- 如何准确判断一个变量是数组类型
var arr=[]
arr instanceof Array //true
typeof arr //object typeof无法准确判断是否是数组
- 写一个原型链继承的例子
function Animal(name){
this.name=name
}
Animal.prototype.run=function(){
console.log('run');
};
function Dog(name) {
Animal.call(this,name)
}
Dog.prototype=new Animal();
let dog=new Dog('wbq');
dog.run();
console.log(dog.name)
- 封装一个DOM查询
<div id="div1">ddd</div>
function Elem(id) {
this.elem=document.getElementById(id);
}
Elem.prototype.html=function (val) {
var elem=this.elem;
if(val){
elem.innerHTML=val;
return this
}else{
return elem.innerHTML
}
}
Elem.prototype.on=function (type,fn) {
var elem=this.elem;
elem.addEventListener(type,fn)
return this
}
var div1=new Elem('div1');
div1.on('click',function () {
alert('click')
}).html('<p>链式操作</p>')
- 描述new一个对象的过程
//创建一个构造函数
function Foo(name,age){
// this={}
this.name=name
this.age=age
// return this
}
var f=new Foo('wbq',10);
- 创建一个新对象:{}
- 将构造函数的作用域赋给新对象(因此this指向这个新对象):this={}
- 执行构造函数中的代码(为这个新对象添加属性):this.xxx=xxx
- 返回新对象:return this
let newFun=function (func) {
//1.新建一个空对象,并将 构造函数的原型对象赋给这个空对象
let obj=Object.create(func.prototype);
//2.执行构造函数,相应参数被传入,并将this的上下文指向新创建的对象obj
var ret=func.call(obj);
//3.如果构造函数返回了对象,就舍弃之前创建的对象obj,newObj = ret
if(typeof ret === 'object') return ret;
else return obj;
}
var foo = function(name){
this.name = name || 'lalaBao';
}
var newObj = newFun(foo);
console.log(newObj);
作用域和闭包
- 执行上下文
- 范围:一段<script>或者一个函数
- 全局:变量定义、函数声明 一段<script>
- 函数:变量定义、函数声明、this、arguments
- this
- this要在执行时才能确认值,定义时无法确认
var a={ name:'A', fn:function () { console.log(this.name) } } a.fn();//A this===a var fn1=a.fn; a.fn.call({name:'B'});//B this==={name:'B'} console.log(fn1());//undefined this===window
- 闭包
- 使用场景
- 返回一个函数,函数作为返回值
function F1() { var a=100; //返回一个函数,函数作为返回值 return function () { console.log(a);//自由变量,去父作用域寻找 } } var f1=F1() var a=200 f1();//100
- 函数作为参数来传递
function F1() { var a=100; return function () { console.log(a) } } var f1=F1() function F2(fn) { var a=200 fn() } F2(f1);//100
- 说一下对变量提升的理解
- 在整个js代码执行前,会先声明带var和带function关键字的变量
- 带var关键字的变量,只是被提前声明,不会被赋值
- 带function关键字的变量,被提前声明,也会给这个函数名赋值
console.log(a);
let a=10;//Uncaught ReferenceError: a is not defined
var a;//undefined
fn('wbq');
function fn(name) {
console.log(name);//wbq
}
- 说明this几种不同的使用场景
- 作为构造函数执行
functon Foo(name){
this.name=name;
}
var f=new Foo('wbq')
- 作为对象属性执行
var obj={
name:'wbq',
printName:function(){
console.log(this.name)
}
}
obj.printName();
- 作为普通函数执行
function fn(){
console.log(this);
}
fn();
- call,apply,bind
function fn1(name,age){
console.log(name);
console.log(this);
}
fn1('wbq');
fn1.call({x:100},'zhangsan',20);
fn1.apply({x:100},['zhangsan',20]);
//bind在函数声明的形式后不可用,必须是函数表达式
var fn2=function (name,age){
console.log(name);
console.log(this);
}.bind({y:200});
fn2('zhangsan',20)
- bind是ES5新出的方法,有些浏览器不兼容
- 用原生js改写bind
Function.prototype.abind = function(context){ self = this; //保存this,即调用bind方法的目标函数 return function(){ return self.apply(context,arguments); }; };
- 创建10个<a>标签,点击时弹出对应序号
错误写法
- 当点击事件发生时,for循环已经执行完毕
- 这时的i已经是循环后的值10
- 所以点击后都弹出10
//使用let形成一个块级作用域
for(let i=0;i<10;i++){
var a=document.createElement('a');
a.innerHTML=i;
a.onclick=function () {
alert(i)
};
document.body.appendChild(a);
}
//使用闭包
定义自执行函数,就是不同调用,只要定义完成,立即执行的函数
for(var i=0;i<10;i++){
var a=document.createElement('a');
a.innerHTML=i;
(function (i) {
a.onclick=function () {
alert(i)
};
})(i);
document.body.appendChild(a);
}
- 如何理解作用域
JavaScript的作用域和作用域链
- JavaScript的作用域指的是变量的作用范围
- 内部作用域由函数的形参,实参,局部变量,函数构成
- 内部作用域和外部的作用域一层层的链接起来形成作用域链
- 当在在函数内部要访问一个变量的时候,首先查找自己的内部作用域有没有这个变量,如果没有就到这个对象的原型对象中去查找,还是没有的话,就到该作用域所在的作用域中找,直到到window所在的作用域,每个函数在声明的时候就默认有一个外部作用域的存在了
①没有块级作用域
if(true){
var name='zhangsan'
}
console.log(name)//'zhangsan'
②只有全局和函数作用域
var a=100;
function fn(){
var a=200;
console.log('fn',a)
}
console.log('global',a)
fn()
- 实际开发中闭包的应用
- 闭包简单的说就是一个函数能访问外部函数的变量,这就是闭包
/闭包实际应用中主要用于封装变量,收敛权限
function isFirstLoad(){
var _list=[];
return function(id){
if(_list.indexOf(id)>=0){
return false;
}else{
_list.push(id);
return true;
}
}
}
//使用
var firstLoad=isFirstLoad();
firstLoad(10);//true
firstLoad(10);//false
//你在 isFirstLoad 函数外面,根本不可能修改掉_list的值
异步和单线程
- 同步和异步的区别是什么?分别举一个同步和异步的例子
- 同步与异步最大的区别是阻塞代码,同步会阻塞代码,而异步不会
- alert是同步,setTimeout是异步
//同步
console.log(100);
alert(200)
console.log(300);
//异步
console.log(100);
setTimeout(function(){
console.log(200);
},1000)
console.log(300);
- 执行第一行,打印100
- 执行setTimeout后,传入setTimeout的函数会被暂存起来,不会立即执行(单线程的特点,不能同时执行两个任务)
- 执行最后一行,打印300
- 待所有任务执行完,处于空闲状态,才执行暂存的任务
- 暂存的setTimeout无等待时间,立即执行
- 前端使用异步的场景有哪些
- 定时任务:setTimeout、setInterval
- 网络请求:ajax请求,fetch(),动态img加载
- 事件绑定
它们共同的特点是需要等待,由于js是一个单线程语言,为了避免阻塞,所以要使用异步
//fetch
console.log('start');
fetch("./test-test.json").then(res=>res.json())
.then(data=>{
console.log(data)
})
.catch(error=>{
console.log('error')
})
console.log('end');
//img loaded
console.log('start');
var img=document.createElement('img');
img.onload=function () {
console.log('loaded')
};
img.src='https://box.bdimg.com/static/fisp_static/common/img/searchbox/logo_news_276_88_1f9876a.png'
console.log('end');
document.body.appendChild(img);
//事件
<button id="btn">点击</button>
console.log('start');
var btn=document.getElementById('btn');
btn.addEventListener('click',function () {
console.log('clicked')
})
console.log('end');
其它知识点
- 日期
- Date.now();//1543806595543,获取当前时间的毫秒数
- var dt=new Date();//2018-12-03T03:09:55.536Z,获取Date对象
- dt.getTime();//1543806641907,获取毫秒数
- dt.getFullYear();//2018,年
- dt.getMonth();//11,月(0-11)
- dt.getDate();//3,日(0-31)
- dt.getHours();//11, 小时(0-23)
- dt.geetMinutes();//13,分钟(0-59)
- dt.getSeconds();//20,秒(0-59)
- 数组
- forEach:遍历所有元素
- every:判断所有元素是否都符合条件
- some:判断是否有至少一个元素符合条件
- sort:排序
- map:对元素重新组装,生成新数组
- filter:过滤符合条件的元素
let arr=[1,2,3,4]; arr.forEach((item,index)=>console.log(item,index));//1,0 2,1 3,2 4,3 let arr=[1,2,3,4]; arr.every((item,index)=>console.log(item>0))//true let arr=[1,2,3,4]; arr.some((item,index)=>console.log(item>1));//false true true true let arr=[42,3,1,2,63,7,18]; console.log(arr.sort((a,b)=>a-b));//[ 1, 2, 3, 7, 18, 42, 63 ] let arr=[42,3,1,2,63,7,18]; console.log(arr.map((item)=>item*3));//[ 126, 9, 3, 6, 189, 21, 54 ] let arr=[42,3,1,2,63,7,18]; console.log(arr.filter((item)=>item>38));//[ 42, 63 ]
- 对象API
- for...in
- 获取2017-07-13格式的日期
function formatDate(dt) {
if (!dt) {
dt = new Date();
}
var year = dt.getFullYear()
var month = dt.getMonth() + 1
var date = dt.getDate()
if (month < 10) {
month = '0' + month
}
if (date < 10) {
date = '0' + date
}
return year + '-' + month + '-' + date
}
var dt
dt = new Date()
alert(formatDate(dt))
function getYearMonthDate(dt=new Date()) {
let year=dt.getFullYear();
let month=dt.getMonth()+1;
let date=dt.getDate();
if(month<10){
month='0'+month
}
if(date<10){
date='0'+date
}
return `${year}-${month}-${date}`
}
console.log(getYearMonthDate());
- 获取随机数,要求是长度一致的字符串格式
var random = Math.random()
random = random + '0000000000' //10个0
random=random.slice(0,10) //slice() 方法返回一个从0开始到1结束(不包括结束)选择的数组的一部分,浅拷贝到一个新数组对象。原始数组不会被修改
console.log(random);
- 写一个能遍历对象和数组的通用forEach函数
function myForEach(obj, fn) {
var key
if (obj instanceof Array) { //判断是否为数组
obj.forEach(function (item, index) {
fn(index, item)
})
} else { //不是数组就是对象
for (key in obj) {
fn(key, obj[key])
}
}
}
var arr = [1, 2, 3] //参数顺序换了,为了和对象的遍历格式一致
myForEach(arr, function (index, item) {
console.log(index, item);
})
var obj = {x: 100, y: 200}
myForEach(obj, function (key, value) {
console.log(key, value);
})
DOM和BOM
- DOM是哪种基本的数据结构:树
- DOM操作常用的API有哪些
- 创建节点:createElement
- 添加节点:appendChild
- 获取父元素:parentElement
- 获取子元素:childNodes
- 移除子元素:removeChild
- 设置属性:setAttribute
- 获取属性:getAttribute
- DOM节点的attr和property有何区别
- property是一个JS对象的属性的修改
- Attribute是HTML标签属性的修改
- 如何检测浏览器的类型:navigator.userAgent
var ua=navigator.userAgent
var isChrome=ua.indexOf('Chrome')
console.log(isChrome);
- 拆解url的各部分
//location
console.log(location.href);
console.log(location.protocol); //协议 http https
console.log(location.pathname); //域名之后的路径
console.log(location.search);
console.log(location.hash);
事件
- 编写一个通用的事件监听函数
function bindEvent(elem,type,fn) {
elem.addEventListener(type,fn)
}
var a=document.getElementById('link1');
bindEvent(a,'click',function (e) {
e.preventDefault();//阻止默认行为
console.log('clicked')
});
- 描述事件冒泡流程
- DOM树形结构
- 事件冒泡
- 阻止冒泡:e.stopPropagation()
- 对于一个无限下拉加载图片的页面,如何给每个图片绑定事件
利用事件委托,事件都有事件冒泡机制,给父级元素绑定事件,通过e.target找到事件源
- 代理的两个优点
- 代码简洁
- 减少浏览器内存占用
var oUl=document.getElementsByTagName('ul')[0];
oUl.addEventListener('click',function (e) {
let target=e.target;
if(target.nodeName=='LI'){
console.log(target.innerHTML)
}
});
存储与Ajax
- 请描述一下cookie,sessionStorage,和localStorage的区别
- 容量区别,cookie为4k,localStorage和sessionStorage为5M
- cookie每次请求都会被携带在ajax中,
- localStorage和sessionStorage不会被携带只作为存储使用
- API易用性
- 手动编写一个ajax,不依赖第三方库
var xhr=new XMLHttpRequest()
xhr.open('GET','/api',false)
xhr.onreadystatechange=function(){ //这里的函数异步执行 if(xhr.readyState==4){
if(xhr.status==200){
alert(xhr.responseText)
}
}
}
xhr.send(null)
-
跨域的几种实现方式
浏览器有同源策略,不允许ajax访问其他域接口
跨域条件:协议、域名、端口,有一个不同就算跨域
- JSONP
- 服务器端设置http header:CORS(跨域资源共享)
- proxy代理模式
devServer: {
historyApiFallback: true,
noInfo: true,
overlay: true,
proxy:{
"/api/":{
target:"http://testmove.kandayi.com.cn/",
changeOrigin:true
}
}
},
模块化
模块化本身就是一个面试的问题
- 不会模块化代码写法
- 这些代码中的函数必须是全局变量,才能暴露给使用方。容易造成变量污染
- a.js知道要引用a-util.js,但是他知道还需要依赖于util.js吗
//util.js
function getFormatDate(dt=new Date(),type=1) {
let year=dt.getFullYear();
let month=dt.getMonth()+1;
let date=dt.getDate();
if(type==1){
if(month<10){
month='0'+month
}
if(date<10){
date='0'+date
}
return `
${year}-${month}-${date}
`
}else{
return `
${year}年${month}月${date}日
`
}
}
//a-util.js
function aGetFormatDate(date) {
return getFormatDate(date,2)
}
//a.js
var dt=new Date();
console.log(aGetFormatDate(dt));
//index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<script src="util.js"></script>
<script src="a-util.js"></script>
<script src="a.js"></script>
<script>
</script>
</body>
</html>
- 使用模块化解决以上问题
- 直接<script data-main="./main.js"></script>,其他的根据依赖关系自动引用
- 那两个函数,没必要做成全局变量,不会带来污染和覆盖
//util.js
define(function () {
return {
getFormatDate:function (date,type) {
let year=date.getFullYear();
let month=date.getMonth()+1;
let day=date.getDate();
if(type==1){
if(month<10){
month='0'+month
}
if(day<10){
day='0'+day
}
return `
${year}-${month}-${day}
`
}else{
return `
${year}年${month}月${day}日
`
}
}
}
})
//a-util.js
define(['./util.js'],function (util) {
return {
aGetFormatDate:function (date) {
return util.getFormatDate(date,2)
}
}
})
//a.js
define(['./a-util.js'],function (aUtil) {
return {
printDate:function (date) {
console.log(aUtil.aGetFormatDate(date))
}
}
})
//main.js
require(['./a.js'],function (a) {
var date=new Date();
a.printDate(date);
});
//test.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<script src="https://cdn.bootcss.com/require.js/2.3.6/require.min.js" data-main="./main.js"></script>
<script>
</script>
</body>
</html>
- AMD
- A:异步 M:模块 D:定义
- require.js <a>requirejs.org</a>
- 全局define函数
- 全局require函数
- 依赖JS会自动异步加载
- 使用requirejs完成刚才的例子
- CommonJS
- CommonJS是Nodejs模块化规范,现在被大量用前端
- 前端开发依赖的插件和库,都可以从npm获取
- 构建工具高度自动化,使npm成本非常低
- CommonJS本身不会异步加载JS,而是一次性同步加载出来
- module.exports={aaa:...,bbb:...}输出模块,require(xxx.js)引用模块
- AMD和CommonJS的使用场景
- 需要异步加载,使用AMD
- 使用npm后建议使用CommonJS(webpack,Node.js)
//a-util.js
var getFormatDate=require('util');
module.exports={
aGetFormatDate:function(date) {
return getFormatDate(date,2)
}
}
//util.js
module.exports={
getFormatDate:function (dt=new Date(),type=1){
let year=dt.getFullYear();
let month=dt.getMonth()+1;
let date=dt.getDate();
if(type==1){
if(month<10){
month='0'+month
}
if(date<10){
date='0'+date
}
return `
${year}-${month}-${date}
`
}else{
return `
${year}年${month}月${date}日
`
}
}
}
构建工具
- grunt
- gulp
- fis3:百度内部
- webpack
- new webpack.optimize.UglifyJsPlugin():js压缩
常用Git命令
- git add .:添加所有文件
- git checkout xxx:还原某个文件
- git commit -m 'xxx':提交文件到本地仓库
- git push origin master:提交文件到远程仓库
- git pull origin master:拉取远程仓库文件到本地
- git branch:看当前分支
- git checkout -b xxx:新建分支
- git checkout xxx:切换到某个分支
- git merge xxx:合并分支
- git status:查看状态
- git clone:拷贝线上项目地
- cat README.md:查看文件
- vi README.md:往文件里插入内容
- git diff:查看文件修改内容
https://git.coding.net/limiywbq/test.git
mkdir js-git-test
cd js-git-test
git init
echo "# js-git-test" >>README.md
git add README.md
cat README.md
git status
git commit -m 'first commit'
git push origin master
上线流程要点
- 将测试完成的代码提交到git版本库的master分支
- 将当前服务器的代码全部打包并记录版本号,备份
- 将master分支的代码提交覆盖到线上服务器,生成新版本号
回滚流程要点
- 将当前服务器代码打包并记录版本号,备份
- 将备份的上一个版本号解压,覆盖到线上服务器,并生成新的版本号
linux基本命令
mkdir a:创建文件夹
ls:查看文件夹的名字
ll:查看文件夹下的内容
cd a:进入a文件夹
pwd:查看文件夹所有文件路径
cd ..:返回上一级文件夹
rm -rf a:删除文件夹a
vi a.js:创建编辑a.js
i:在文件里输入内容
ESC:wq:退出并保存文件
cat a.js:查看文件全部内容
rm a.js:删除文件
页面加载与性能优化
-
加载资源的形式
- 输入url(或跳转页面)加载HTML
- 加载HTML中的静态资源 script link img等
-
加载一个资源的过程
- 浏览器根据DNS服务器得到域名的IP地址
- 向这个IP的机器发送http请求
- 服务器收到、处理并返回http请求
- 浏览器得到返回内容
-
浏览器渲染页面的过程
- 根据HTML结构生成DOM Tree
- 根据CSS生成CSSOM
- 将DOM和CSSOM整合成RenderTree
- 根据RenderTree渲染和展示
- 遇到<script>时,会执行并阻塞渲染,所以<script>放在<body>即将结束的位置
- 因为js有权利改变dom结构,如果同时进行会发生冲突
- 从输入url到得到HTML的详细过程
- 浏览器根据DNS服务器得到域名的IP地址
- 向这个IP的机器发送http请求
- 服务器收到、处理并返回http请求
- 浏览器得到返回内容
- window.onload和DOMContentLoaded的区别
- window.onload,页面的全部资源加载完成才会执行,包括图片视频等
- DOMContentLoaded,DOM渲染完即可执行,此时图片视频等可能还没加载完
window.addEventListener('load',function(){
//页面的全部资源加载完成才会执行,包括图片视频等
})
document.addEventListener('DOMContentLoaded',function(){
//DOM渲染完即可执行,此时图片视频等可能还没加载完
})
- 性能优化
原则:
- 多使用内存,缓存或者其他方法
- 减少CPU计算,减少网络
从哪里入手
- 加载页面和静态资源
- 页面渲染
- 静态资源的压缩合并
- 静态资源缓存
- 使用CDN让资源加载更快
- 使用SSR后端渲染,数据直接输出到HTML中
性能优化的几个示例
- CSS放前面,JS放后面
- 合并js文件,减少请求次数(如webpack的plugins:[new webpack.optimize.UglifyJsPlugin()])
- 通过连接名称控制缓存<script src="abc_1.js"></script>只有内容改变时才更改名称<script src="abc_2.js"></script>
- 使用CDN<scriptsrc="https://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/js/bootstrap.min.js"></script>
- 使用SSR后端渲染(Vue、React):可以把后端数据直接渲染到页面中,不必要通过ajax调取后端数据,可以很大程度提高页面性能
- 懒加载
<img id="img1" src="preview.png" data-realsrc="abc.png"/>
<script>
var img1=document.getElementById('img1');
img1.src=img1.getAttribute('data-realsrc');
</script>
- 减少DOM查询,对DOM查询做缓存
//未缓存DOM查询
var i
for(i=0;i<document.getElementsByTagName('p').length;i++){
//todo
}
//缓存了DOM查询
var pList=document.getElementsByTagName('p')
var i
for(i=0;i<pList.length;i++){
//todo
}
这样可以避免多次执行DOM查询
- 合并DOM插入
var listNode=document.getElementById('list')
//插入10个li标签
var frag=document.createDocumentFragment();
var x,li
for(x=0;x<10;x++){ li=document.createElement('li')
li.innerHTML='List item'+x frag.appendChild(li) }
listNode.appendChild(frag)
安全性与面试技巧
-
XSS跨站请求攻击
- 写一篇文章,同时偷偷插入一段<script>
- 攻击代码中,获取cookie,发送自已的服务器
- 发布文章被人查看
- 会把查看者的cookie发送到攻击者的服务器
解决方法:
- 前端替换关键字,如替换<为<,替换>为>
- 后端替换
-
XSRF跨站请求伪造
- 已登录一个网站正在浏览商品
- 该网站付费接口是xxx.com/pay?id=100但是没有任何验证,
- 然后你收到了一封邮件,隐藏<img src=xxx.com/pay?id=100>
- 当查看邮件时就已完成付费了
解决方法:
增加验证流程,如输入指纹,密码,短信验证码
网友评论