Skip to content

Latest commit

 

History

History
1257 lines (1058 loc) · 37.1 KB

前端面试题.md

File metadata and controls

1257 lines (1058 loc) · 37.1 KB
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no">

第一题:

var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
return i;
    }
}
a[8]();

问:结果是多少?

正确答案是10,我也知道是怎么来的,可是面试官说,上面的代码如何能获取正确结果,请用ES5和ES6分别作答,这个我就不懂了。

第二题:

console.log(text)
function text() {}
var text = 0;
console.log(text)

结果我是知道的,第一个就是function,第二个是0,但是面试官问我为什么?如果把第二行和第三行互换,结果是怎样,为什么?

第一道题考你对作用域的理解,结果是10,因为作用域分为全局作用域和函数作用域,这里的i当你调用这个函数的时候,已经是循环后的i了,也就是10。

es5的方法是用立即执行函数创建独立作用域,如下

for (var i = 0; i < 10; i++) {
    a[i] = function bar(i){
        return function () {
            return i;
        };
    }(i);
}
a[8]();

es6的方法直接var改为let,这样子for 的 i 变量就有块作用域,如下

var a = [];
for (let i = 0; i < 10; i++) {
    a[i] = function () {
        return i;
    }
}

第二道题考你变量名和函数声明都要提升,为什么?因为代码解释器会预编译成:

var text;
function text() {}
console.log(text)
text = 0;
console.log(text)

交换第二行和第三行位置,如下:

console.log(text)
var text = 0;
function text() {}
console.log(text);

解释器同样会预编译成:

var text;
function text() {}
console.log(text)
text = 0;
console.log(text);

所以结果还是一样

CSS盒模型

在一个文档中,每个 元素 都被表示为 一个矩形的盒子

每个盒子有四个边:外边距边, 边框边, 内填充边 与 内容边。

根据 W3C 的规范,元素内容占据的空间是由 width 属性设置的,而内容周围的 padding 和 border 值是另外计算的。不幸的是,IE5.X 和 6 在怪异模式中使用自己的非标准模型。这些浏览器的 width 属性不是内容的宽度,而是内容、内边距和边框的宽度的总和。

解决IE8及更早版本不兼容问题可以在HTML页面声明 <!DOCTYPE html>即可。

box-sizing 属性用于更改用于计算元素宽度和高度的默认的 CSS 盒子模型。

CSS未知宽高元素水平垂直居中

博客园

DOM事件中target和currentTarget的区别

博客园

怎么解决跨域的,以及后续JSONP的原理和实现以及cors怎么设置

博客园

JavaScript 深拷贝

element ui checkBoxs  是否选中为true、false   需要转换成接口接受的0、1
function inCopy(obj1,obj2) {
    var obj1 = obj1 || {};//容错处理
    for (var k in obj2) { 
        if(obj2.hasOwnProperty(k)){ //只拷贝实例属性,不进行原型的拷贝
            if(typeof obj2[k] == 'object') { //引用类型的数据单独处理
                obj1[k] = Array.isArray(obj2[k])?[]:{};
                inCopy(obj1[k],obj2[k]); //递归处理引用类型数据
            }else{
                obj1[k] = obj2[k]; //值类型的数据直接进行拷贝
            }
        }
    }
}
function deepClone(source){    
  if(!source && typeof source !== 'object'){      
    throw new Error('error arguments', 'shallowClone');    
  }    
  var targetObj = Array.isArray(source) ? [] : {};    
  for(var keys in source){       
    if(source.hasOwnProperty(keys)){          
      if(source[keys] && typeof source[keys] === 'object'){  
        targetObj[keys] = deepClone(source[keys]);    //递归      
      }else{            
        targetObj[keys] = source[keys];         
      }       
    }    
  }    
  return targetObj; 
}

webpack入门

segmentfault

tween 缓动算法

张鑫旭

tween算法原理解析1 tween算法原理解析2 tween算法原理解析3

博客园 博客园 博客园 CSDN

弹跳效果:easeOutbounce

微信 QQ 登录接口开发

博客园 博客园

vue-cli .vue组件上传到npm发布

慕课手记

获取css3属性translateX

'translateX(-230px)'.match(/translateX\((.*)\)/)[1]

alert((element.currentStyle? element.currentStyle : window.getComputedStyle(element, null)).height);

正则 test、exec、match

正则 test、exec、match (MDN)

前端判断是Android还是ios

网址

<script type="text/javascript">
var u = navigator.userAgent;
var isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > -1; //android终端
var isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //ios终端
alert('是否是Android:'+isAndroid);
alert('是否是iOS:'+isiOS);
</script>
<script type="text/javascript">
//判断访问终端
var browser={
    versions:function(){
        var u = navigator.userAgent, app = navigator.appVersion;
        return {
            trident: u.indexOf('Trident') > -1, //IE内核
            presto: u.indexOf('Presto') > -1, //opera内核
            webKit: u.indexOf('AppleWebKit') > -1, //苹果、谷歌内核
            gecko: u.indexOf('Gecko') > -1 && u.indexOf('KHTML') == -1,//火狐内核
            mobile: !!u.match(/AppleWebKit.*Mobile.*/), //是否为移动终端
            ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), //ios终端
            android: u.indexOf('Android') > -1 || u.indexOf('Adr') > -1, //android终端
            iPhone: u.indexOf('iPhone') > -1 , //是否为iPhone或者QQHD浏览器
            iPad: u.indexOf('iPad') > -1, //是否iPad
            webApp: u.indexOf('Safari') == -1, //是否web应该程序,没有头部与底部
            weixin: u.indexOf('MicroMessenger') > -1, //是否微信 (2015-01-22新增)
            qq: u.match(/\sQQ/i) == " QQ" //是否QQ
        };
    }(),
    language:(navigator.browserLanguage || navigator.language).toLowerCase()
}
</script>

前端JavaScript判断键盘是否收起

https://www.cnblogs.com/wangyihong/p/7514304.html

http://www.cnblogs.com/dtdxrk/p/3682352.html

https://segmentfault.com/q/1010000004307305

IOS上第三方输入法,用JS监听不到键盘不事件

sf

贝塞尔曲线测试网站

http://cubic-bezier.com/

转义符 HTML

//去掉html标签
function removeHtmlTab(tab) {
 return tab.replace(/<[^<>]+?>/g,'');//删除所有HTML标签
}

//普通字符转换成转意符
function html2Escape(sHtml) {
 return sHtml.replace(/[<>&"]/g,function(c){return {'<':'&lt;','>':'&gt;','&':'&amp;','"':'&quot;'}[c];});
}

//转意符换成普通字符
function escape2Html(str) {
 var arrEntities={'lt':'<','gt':'>','nbsp':' ','amp':'&','quot':'"'};
 return str.replace(/&(lt|gt|nbsp|amp|quot);/ig,function(all,t){return arrEntities[t];});
}

Axios、jQuery formatData图片上传

var ff = e.target.files[0];
var reader = new FileReader();
reader.readAsDataURL(ff);


let param = new FormData()  // 创建form对象
param.append('upfile', ff, ff.name)  // 通过append向form对象添加数据
let config = {
  headers: {'Content-Type': 'multipart/form-data'}
}
// var params = new URLSearchParams();
// params.append('upfile', ff);
axios.post('/oss/picupload', param, config
// {
//     'upfile': ff
// }
)
.then(function(res){
    console.log(res);
});



var data = new FormData();  
data.append('upfile', ff)
$.ajax({
    url: '/oss/picupload',
    type: 'post',
    processData: false,  
    contentType: false,
    data: 
        data
        // JSON.stringify({
        //     'upfile': 'ff'
        // })
        ,
    dataType: 'json',
    success: function(res){
        console.log(res);
    }
});

获取各类宽高

window.getComputedStyle
获取width、height
window.getComputedStyle("元素", "伪类"); IE9以下不支持,需使用element.currentStyle; 其中暂时测试IE9不支持伪类
张鑫旭文章:http://www.zhangxinxu.com/wordpress/2012/05/getcomputedstyle-js-getpropertyvalue-currentstyle/
clientWidth和clientHeight
表示内容区域的高度和宽度,包括padding大小,但是不包括边框和滚动条
全浏览器支持
张鑫旭文章:http://www.zhangxinxu.com/wordpress/2011/09/cssom%E8%A7%86%E5%9B%BE%E6%A8%A1%E5%BC%8Fcssom-view-module%E7%9B%B8%E5%85%B3%E6%95%B4%E7%90%86%E4%B8%8E%E4%BB%8B%E7%BB%8D/
offsetHeight
是一个只读属性,它返回该元素的像素高度,高度包含该元素的垂直内边距和边框,且是一个整数
MDN:https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLElement/offsetHeight
outHeight
无此属性
Element.getBoundingClientRect()方法返回元素的大小及其相对于视口的位置。
(其中width:包含width + padding + border)

HTML5重力

http://www.html5tricks.com/html5-ball-pool.html http://blog.csdn.net/lufy_legend/article/details/7654607 http://www.htmleaf.com/jQuery/Layout-Interface/201506142030.html

正则匹配HTML标签

var pattern = /<("[^"]*"|'[^']*'|[^'">])*>/

http://imweb.io/topic/56e804ef1a5f05dc50643106

div contenteditable 默认文字设置

<div class="count-string" contenteditabel="true" placeholder="输入文字..."></div>
.count-string:empty:before{
	content: attr(placeholder);
	color: #757575;
}

document.documentElement和document.body区别介绍

区别:

body是DOM对象里的body子节点,即 标签; documentElement 是整个节点树的根节点root,即 标签;

没使用DTD情况即怪异模式BackCompat下:

document.documentElement.clientHeight=0   document.body.clientHeight=618 

使用DTD情况即标准模式CSS1Compat下:

document.documentElement.clientHeight=618   document.body.clientHeight=28(表示内容的高度) 

HTML标签与转义字符

function HTMLEncode(html) {
    var temp = document.createElement("div");
    (temp.textContent != null) ? (temp.textContent = html) : (temp.innerText = html);
    var output = temp.innerHTML;
    temp = null;
    return output;
}

var tagText = "<p><b>123&456</b></p>";
console.log(HTMLEncode(tagText));//&lt;p&gt;&lt;b&gt;123&amp;456&lt;/b&gt;&lt;/p&gt; 
function HTMLDecode(text) { 
    var temp = document.createElement("div"); 
    temp.innerHTML = text; 
    var output = temp.innerText || temp.textContent; 
    temp = null; 
    return output; 
} 
var tagText = "<p><b>123&456</b></p>";
var encodeText = HTMLEncode(tagText);
console.log(encodeText);//&lt;p&gt;&lt;b&gt;123&amp;456&lt;/b&gt;&lt;/p&gt;
console.log(HTMLDecode(encodeText)); //<p><b>123&456</b></p> 

getComputedStyle、currentStyle

function getStyle(element, css){
  var elementStyle = window.getComputedStyle ? window.getComputedStyle(element, null) : element.currentStyle;
  var elementCss = elementStyle.getPropertyValue ? elementStyle.getPropertyValue(css) : elementStyle.getAttribute(css);   // getPropertyValue、getAttribute都支持 "background-color" 写法(不考虑IE6)

  return elementCss;
}

参考张鑫旭

Sublime Text 快捷键

http://www.cnblogs.com/fan-fan/p/4526817.html

http://www.mamicode.com/info-detail-651198.html

javascript数据类型

1、值类型:即5种基本类型(string,number,boolean,null,undefined);

2、引用类型:即数组、函数、对象共三种。这三种类型的处理与值类型会有很大的差别。
typeof "s"       // string
typeof 1         // number 
typeof null      // object
typeof undefined // undefined
typeof true      // boolean
typeof function(){} // function
typeof {}        // object
typeof []        // object

检测浏览器刷新、前进、后退

window.onbeforeunload = function (e) {
  e = e || window.event;

  // 兼容IE8和Firefox 4之前的版本
  if (e) {
    e.returnValue = '关闭提示';
  }
  console.log(111);
  // Chrome, Safari, Firefox 4+, Opera 12+ , IE 9+
  return '关闭提示';
};

统计代码行数

find . "(" -name "*.m" -or -name "*.mm" -or -name "*.cpp" -or -name "*.h" -or -name "*.rss" -or -name "*.xib"-or -name "*.swift" ")" -print | xargs wc -l

使用axios实现上传图片进度条

https://www.cnblogs.com/wxb-it/p/7774758.html

[http://www.cnblogs.com/ypinchina/p/7827169.html](vue多文件上传进度条 进度不更新问题)

var form = new FormData()
form.append('file', vm.$refs.upload.files[0])
form.append('id', id)
form.append('type', type)
var config = {
 onUploadProgress: progressEvent => {
  var complete = (progressEvent.loaded / progressEvent.total * 100 | 0) + '%'
  this.progress = complete
 }
}
axios.post(`api/uploadFile`,
 form, config).then((res) => {
 if (res.data.status === 'success') {
  console.log('上传成功')
 }
})

支付宝支付

// 跳转支付宝
that.$toast(res.data.msg);
defaultFn.AxiosPost('/alipay/alipayGenOrderInfoForH5', {
	'userId': localStorage.getItem('userId'),
	'subject': '充值',
	'cash': that.projectDetail.project.pay,
	'return_url': window.location.href ,
	'paymentType': '2'
})
.then(ress => {
	if (ress.data.code != 0) {
		that.$toast(ress.data.msg);
	}else {
		let payData = ress.data.data;
		let payDataArr = payData.split('&');
		let payDataObj = {};
		for(let i in payDataArr){
			payDataObj[payDataArr[i].split('=')[0]] = payDataArr[i].split('=')[1];
		}
		// console.log(payDataObj);

		// Axios.post('https://openapi.alipay.com/gateway.do?' + payData)
		// .then(resss => {
		// 	console.log(resss);
		// });

		// https://openapi.alipay.com/gateway.do

		window.location.href = 'https://openapi.alipay.com/gateway.do?' + payData;

	}
});

QQ授权登陆

let hashUrl = window.location.href.split('#')[1];
let hashUrlJson = {
	'access_token': '',
	'expires_in': '',
	'state': ''
};
hashUrlJson.access_token = hashUrl.split('&')[0].split('=')[1];
hashUrlJson.expires_in = hashUrl.split('&')[1].split('=')[1];
hashUrlJson.state = hashUrl.split('&')[2].split('=')[1];			

defaultFn.AxiosPost('/account/qq', hashUrlJson)
.then(res => {
	// console.log(res);
	switch(res.data.code){
		// console.log(res);
		case 0: 
				localStorage.setItem('token', res.data.token);
				localStorage.setItem('userId', res.data.data.id);
				// console.log(res);
				window.location.href = decodeURIComponent(res.data.state);
				break;
		case 1: 
				that.showBindPhone = true;
				that.type = res.data.state;
				that.id = res.data.openId;
				break;
		case 2: 
				that.showBindPhone = true;
				that.type = res.data.state;
				that.id = res.data.openId;
				break;
		case 4: 
				that.$toast(res.data.msg);
				setTimeout(function(){
					window.location.href = decodeURIComponent(res.data.state);
				}, 1000);
				break;
		case -1: 
				that.$toast(res.data.msg);
				setTimeout(function(){
					window.location.href = decodeURIComponent(res.data.state);
				}, 1000);
				break;
	}
});

微信授权登陆

let hashUrl = window.location.href.split('?')[1];
let hashUrlJson = {
	'code': '',
	'type': 'SRV',
	'state': ''
};
hashUrlJson.code = hashUrl.split('&')[0].split('=')[1];
hashUrlJson.state = hashUrl.split('&')[1].split('=')[1];

defaultFn.AxiosPost('/account/wx', hashUrlJson)
.then(res => {
	// console.log(res);
	switch(res.data.code){
		// console.log(res);
		case 0: 
				localStorage.setItem('token', res.data.token);
				localStorage.setItem('userId', res.data.data.id);
				// console.log(res);
				window.location.href = decodeURIComponent(res.data.state);
				break;
		case 1: 
				that.showBindPhone = true;
				that.type = res.data.state;
				that.id = res.data.openId;
				break;
		case 2: 
				that.showBindPhone = true;
				that.type = res.data.state;
				that.id = res.data.openId;
				break;
		case -1: 
				that.$toast(res.data.msg);
				setTimeout(function(){
					window.location.href = decodeURIComponent(res.data.state);
				}, 1000);
				break;
		case 4: 
				that.$toast(res.data.msg);
				setTimeout(function(){
					window.location.href = decodeURIComponent(res.data.state);
				}, 1000);
				break;
	}
});

利用nodejs监控文件变化并使用sftp上传到服务器

// 首先,我们使用npm 安装两个别人封装好的模块。
npm install ssh2-sftp-client
npm install gaze

// 第一个模块的作用是sftp上传文件,

第二个模块的作用就是监听文件变化了。当然,你也可以采用node自带fs模块。
gaze(['你的文件路径/*.*','还可以使用数组的方式监听多个文件夹/app.js'], function(err, watcher) {
 let watched = this.watched();
 //监听文件的变化
 this.on('changed', (filepath) => {
 //romotePath是我文件的远程位置
 let romotePath = '/root' + filepath.substr(15);
 //put为上传文件的函数,下面会讲 
 put(filepath,romotePath);
 console.log(filepath + ' was changed');
 });
});
function put(localPath,romotePath){
 let sftp = new Client();
 sftp.connect({
 host: '你的服务器地址',
 port: '端口,没改过的话是22',
 username: '连接的用户名',
 password: '密码'
 }).then(() => {
 return sftp.put(localPath,romotePath);
 }).then(() =>{
 console.log("上传完成");
 }).catch((err) => {
 console.log(err, 'catch error');
 });
}

参考:http://www.jb51.net/article/105991.htm

函数节流与函数防抖

函数节流与函数防抖

    _debounce(fn,wait){
      // 全部timer
      let that = this;
      // that.timer = null;
        return function(){
          console.log(22)
            clearTimeout(that.timer)
            that.timer = setTimeout(()=>{
              // clearTimeout(timer)
                fn()
            },wait)
        }
    },
    _log(){
        console.log(1)
    },
    
    // @click="_debounce(_log, 1000)()"

ios webview调用js

<!doctype html>
<html><head>
    <meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1.0, maximum-scale=1.0">
	<style type='text/css'>
		html { font-family:Helvetica; color:#222; }
		h1 { color:steelblue; font-size:24px; margin-top:24px; }
		button { margin:0 3px 10px; font-size:12px; }
		.logLine { border-bottom:1px solid #ccc; padding:4px 2px; font-family:courier; font-size:11px; }
	</style>
</head><body>
	<h1>WebViewJavascriptBridge Demo</h1>
	<script>
	window.onerror = function(err) {
		log('window.onerror: ' + err)
	}

    function setupWebViewJavascriptBridge(callback) {
        if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }
        if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }
        window.WVJBCallbacks = [callback];
        var WVJBIframe = document.createElement('iframe');
        WVJBIframe.style.display = 'none';
        WVJBIframe.src = 'https://__bridge_loaded__';
        document.documentElement.appendChild(WVJBIframe);
        setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)
    }

    setupWebViewJavascriptBridge(function(bridge) {
		var uniqueId = 1
		function log(message, data) {
			var log = document.getElementById('log')
			var el = document.createElement('div')
			el.className = 'logLine'
			el.innerHTML = uniqueId++ + '. ' + message + ':<br/>' + JSON.stringify(data)
			if (log.children.length) { log.insertBefore(el, log.children[0]) }
			else { log.appendChild(el) }
		}

		bridge.registerHandler('testJavascriptHandler', function(data, responseCallback) {
			log('ObjC called testJavascriptHandler with', data)
			var responseData = { 'Javascript Says':'Right back atcha!' }
			log('JS responding with', responseData)
			responseCallback(responseData)
		})

		document.body.appendChild(document.createElement('br'))

		var callbackButton = document.getElementById('buttons').appendChild(document.createElement('button'))
		callbackButton.innerHTML = 'Fire testObjcCallback'
		callbackButton.onclick = function(e) {
			e.preventDefault()
			log('JS calling handler "testObjcCallback"')
			bridge.callHandler('testObjcCallback', {'foo': 'bar'}, function(response) {
				log('JS got response', response)
			})
		}
	})
	</script>
	<div id='buttons'></div> <div id='log'></div>
</body></html>



    setupWebViewJavascriptBridge(callback) {
        if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }
        if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }
        window.WVJBCallbacks = [callback];
        var WVJBIframe = document.createElement('iframe');
        WVJBIframe.style.display = 'none';
        WVJBIframe.src = 'https://__bridge_loaded__';
        document.documentElement.appendChild(WVJBIframe);
        setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)
    }

 this.setupWebViewJavascriptBridge(function(bridge) {
        // var uniqueId = 1
        // function log(message, data) {
        //   var log = document.getElementById('log')
        //   var el = document.createElement('div')
        //   el.className = 'logLine'
        //   el.innerHTML = uniqueId++ + '. ' + message + ':<br/>' + JSON.stringify(data)
        //   if (log.children.length) { log.insertBefore(el, log.children[0]) }
        //   else { log.appendChild(el) }
        // }

        bridge.registerHandler('testJavascriptHandler', function(data, responseCallback) {
          // log('log::222::::::', data)
          that.devNo = JSON.stringify(data);
          var responseData = { 'Javascript Says':'Right back atcha!' }
          // log('JS responding with', responseData)
          responseCallback(responseData)
        })

        // document.body.appendChild(document.createElement('br'))

        // var callbackButton = document.getElementById('buttons').appendChild(document.createElement('button'))
        // callbackButton.innerHTML = 'Fire testObjcCallback'
        // callbackButton.onclick = function(e) {
          // e.preventDefault()
          // log('JS calling handler "testObjcCallback"')
          // bridge.callHandler('testObjcCallback', {'foo': 'bar'}, function(response) {
            // log('JS got response', response)
            // that.devNo = response;
          // })
        // }
      })
      

div距离顶部距离

      this.$refs.parent.scrollTop = 20;
      this.element = this.$refs.box;
      while(this.elementTagName != 'body'){
        this.elementTagName = this.element.tagName.toLowerCase();
        if(this.element.parentNode.tagName.toLowerCase() == 'body'){
          this.offsetTop += this.element.offsetTop;
        } else {
          if(this.getStyle(this.element.parentNode, 'position') != 'static'){
            this.offsetTop += this.element.offsetTop;
          }
        }
        console.log("box距离顶部距离:" + this.offsetTop );
        console.log(this.element.parentNode.tagName.toLowerCase()  );
        console.log(this.getStyle(this.element, 'position'));
        this.element = this.element.parentNode;
      }

富文本编辑

http://ghmagical.com/iframe/modal/code/:javascript:getSelection

HTMLCSSJS效果

			var selectedRange = '';

			function setFrameEdit() {
				var frame = document.getElementById("edit2");
				var frameDocument = frame.contentDocument;
				frameDocument.designMode = 'on';
				frameDocument.body.style.color = '#fff';
				frameDocument.body.innerHTML = '我是可编辑的iframe';
			};
			setFrameEdit();

//			function getSelectionRect() {
//				if(window.getSelection) {
//					var sel = window.getSelection();
//					if(sel.rangeCount == 0) {
//						return;
//					};
//					var range = sel.getRangeAt(0).cloneRange();
//					if(range.getBoundingClientRect) {
//						var rect = range.getBoundingClientRect();
//						if(rect && rect.left && rect.top && rect.right && rect.bottom)
//							return {
//								left: parseInt(rect.left),
//								top: parseInt(rect.top),
//								width: parseInt(rect.right - rect.left),
//								height: parseInt(rect.bottom - rect.top)
//							};
//					}
//				} else if(document.selection) {
//					var sel = document.selection;
//					if(sel.type != 'Control') {
//						var range = sel.createRange();
//						// IE
//						if(range.boundingLeft || range.boundingTop || range.boundingWidth || range.boundingHeight)
//							return {
//								left: range.boundingLeft,
//								top: range.boundingTop,
//								width: range.boundingWidth,
//								height: range.boundingHeight
//							};
//					}
//				}
//				return false;
//			}

			function setContextMenu() {
				var edit = document.querySelector('.edit');
				edit.oncontextmenu = function(e) {
					e = e || window.event;
					e.preventDefault();
					
					var top = e.pageY || e.clientY + document.body.scrollTop + document.docuemntElement.scrollTop;
					top -= this.scrollTop;
					var left = e.pageX || e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
					left -= this.scrollLeft;
					
					var menu = '<div class="contextmenu" style="background:#fff;color:#333;padding:10px;position:fixed;top:' + top + 'px;left:' + left + 'px">';
					menu += '<a>右键菜单</a>';
					menu += '</div>';
					var box = document.createElement('div');
					box.innerHTML = menu;
					var menuBox = document.querySelector('.contextmenu');
					if(menuBox) {
						menuBox.style.top = top + 'px';
						menuBox.style.left = left + 'px';
					} else {
						document.body.appendChild(box.firstChild);
					}
				};
				window.addEventListener('click',function(e){
					e = e || window.event;
					var menuBox = document.querySelector('.contextmenu');
					if(menuBox){
						var target = e.target;
						var parent = target.parentNode;
						if(parent.nodeName != 'DIV' || !parent.classList.contains('contextmenu')){
							document.body.removeChild(menuBox);
						}
					}
				});
			}
			setContextMenu();

			function deleteRange() {
				if(selectedRange) {
					selectedRange.deleteContents();
				}
			};

			function toTextEnd(elem) {
				if(window.getSelection) {
					elem.setSelectionRange(elem.value.length, elem.value.length);
					elem.focus()
				} else if(document.selection) {
					/*IE下*/
					var range = elem.createTextRange();
					range.moveStart('character', elem.value.length);
					range.collapse(true);
					range.select();
				}
			};

			function setCursorEnd(elem) {
				if(window.getSelection) {
					elem.focus();
					var range = window.getSelection();
					range.selectAllChildren(elem);
					range.collapseToEnd();
				} else if(document.selection) {
					var range = document.selection.createTextRange();
					range.moveToElementText(elem);
					range.collapse(false);
					range.select();
				}
			}

			function selectAllText(elem) {
				if(window.getSelection) {
					elem.setSelectionRange(0, elem.value.length);
					elem.focus();
				} else if(document.selection) {
					var range = elem.createTextRange();
					range.select();
				}
			}

			function deleteRange2() {
				if(selectedRange) {
					var fragment = selectedRange.extractContents();
					document.querySelector('.show span').appendChild(fragment);
				}
			};

			function saveSelection() {
				if(window.getSelection) {
					/*主流的浏览器,包括chrome、Mozilla、Safari*/
					return window.getSelection();
				} else if(document.selection) {
					/*IE下的处理*/
					return document.selection.createRange();
				};
				return null;
			};

			function saveSelection2() {
				if(window.getSelection) {
					/*主流的浏览器,包括chrome、Mozilla、Safari*/
					var sel = window.getSelection();
					if(sel.rangeCount > 0) {
						return sel.getRangeAt(0);
					}
				} else if(document.selection) {
					/*IE下的处理*/
					return document.selection.createRange();
				}
				return null;
			};

			function getRangePosition() {
				console.log(selectedRange);
				if(selectedRange) {
					return {
						startOffset: selectedRange.startOffset,
						endOffset: selectedRange.endOffset,
						collapsed: selectedRange.collapsed,
						startContainer: selectedRange.startContainer.parentNode.nodeName,
						endContainer: selectedRange.endContainer.parentNode.nodeName,
						commonAncestorContainer: selectedRange.commonAncestorContainer.nodeName
					};
				};
				return null;
			};

			function getSelectionPosition() {
				var selection = window.getSelection();
				var p = {
					anchorNode: selection.anchorNode.parentNode.nodeName,
					anchorOffset: selection.anchorOffset,
					focusNode: selection.focusNode.parentNode.nodeName,
					focusOffset: selection.focusOffset
				};
				document.querySelector('.show span').innerHTML = '';
				for(var k in p) {
					document.querySelector('.show span').innerHTML += '</br>' + k + ': <span class="highlight">' + p[k] + '</span>';
				}
			};

			function restoreSelection() {
				var selection = window.getSelection();
				if(selectedRange) {
					try {
						selection.removeAllRanges(); /*清空所有Range对象*/
					} catch(ex) {
						/*IE*/
						document.body.createTextRange().select();
						document.selection.empty();
					};
					/*恢复保存的范围*/
					selection.addRange(selectedRange);
				}
			};

			function insertRange() {
				restoreSelection();
				var content = ''
				if(document.createRange) {
					var range = document.createRange();
					var span = document.querySelector('.edit span');
					var p = document.querySelector('.edit p');
					range.setStart(span.firstChild, 20);
					range.setEnd(p.firstChild, 3);
					content = range.cloneContents();
				};
				document.querySelector('.show span').appendChild(content);
			};
			document.getElementById("setCursorEnd").addEventListener('click', function() {
				setCursorEnd(document.querySelector('.edit'));
			});
			document.getElementById("selectAllText").addEventListener('click', function() {
				selectAllText(document.querySelector('textarea'));
			});
			document.querySelector('.edit').addEventListener('mouseup', function() {
				selectedRange = saveSelection2();
			});
			document.getElementById("getRange").addEventListener('click', function() {
				selectedRange = saveSelection();
				document.querySelector('.show span').innerHTML = selectedRange; //如果是IE下,必须使用selection.text来获取内容
			});
			document.getElementById("getRange2").addEventListener('click', function() {
				selectedRange = saveSelection2();
				document.querySelector('.show span').innerHTML = selectedRange; //如果是IE下,必须使用selection.text来获取内容
			});
			document.getElementById("getOffset").addEventListener('click', function() {
				selectedRange = saveSelection2();
				var p = getRangePosition();
				document.querySelector('.show span').innerHTML = '';
				for(var k in p) {
					document.querySelector('.show span').innerHTML += '</br>' + k + ': <span class="highlight">' + p[k] + '</span>';
				}
			});
			document.getElementById("setToTextEnd").addEventListener('click', function() {
				toTextEnd(document.querySelector('textarea'));
			})
			document.getElementById("setColor").addEventListener('click', function() {
				restoreSelection();
				document.execCommand('forecolor', false, 'red');
			});
		

微信二次分享

npm install weixin-js-sdk -D
// 微信分享
defaultFn.AxiosPost('/account/wxSrvSignature', {
	"url": window.location.href
})
.then(rea=>{
	console.log(rea);

	defaultFn.AxiosPost('/share/queryshareurl', {
		"ioType": 1008,
		"userId": that.dynamic.data.userId,
		"clubId": that.dynamic.data.clubId,
		"clubDynaId": that.dynamic.data.id
	})
	.then(reb=>{
		wx.config({
			debug: false,
			appId: 'wxc94818fe8e0407f1',
			nonceStr: rea.data.noncestr,
			timestamp: rea.data.timestamp,
			signature: rea.data.signature,
			jsApiList: ['onMenuShareTimeline','onMenuShareAppMessage','onMenuShareQQ',  
                        'onMenuShareWeibo','onMenuShareQZone','chooseImage',  
                        'uploadImage','downloadImage','startRecord','stopRecord',  
                        'onVoiceRecordEnd','playVoice','pauseVoice','stopVoice',  
                        'translateVoice','openLocation','getLocation','hideOptionMenu',  
                        'showOptionMenu','closeWindow','hideMenuItems','showMenuItems',  
                        'showAllNonBaseMenuItem','hideAllNonBaseMenuItem']
		});

		wx.ready(function(){
			wx.onMenuShareQZone({
				title: reb.data.data.title,
				desc: reb.data.data.addTitle,
				imgUrl: reb.data.data.icon,
				success: function(){
					// alert("success!!!Q");
				},
				cancel: function(){

				}
			});
			wx.onMenuShareQQ({
				title: reb.data.data.title,
				desc: reb.data.data.addTitle,
				imgUrl: reb.data.data.icon,
				success: function(){
					// alert("success!!!Q");
				},
				cancel: function(){

				}
			});
			wx.onMenuShareAppMessage({
				title: reb.data.data.title,
				desc: reb.data.data.addTitle,
				imgUrl: reb.data.data.icon,
				success: function(){
					// alert("success!!!Q");
				},
				cancel: function(){

				}
			});
			wx.onMenuShareTimeline({
				title: reb.data.data.title,
				desc: reb.data.data.addTitle,
				imgUrl: reb.data.data.icon,
				success: function(){
					// alert("success!!!Q");
				},
				cancel: function(){

				}
			});
		});
	});
});

微信文档

微信分享: https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1483682025_enmey

微信授权登陆:  https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842

微信内公众号支付:  https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6

微信内H5支付:   https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=15_4

函数默认参数值

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/Default_parameters

JavaScript 中函数的参数默认是undefined。然而,在某些情况下可能需要设置一个不同的默认值。这是默认参数可以帮助的地方。

以前,一般设置默认参数的方法是在函数体测试参数是否为undefined,如果是的话就设置为默认的值。下面的例子中,如果在调用multiply时,参数b的值没有提供,那么它的值就为undefined。如果直接执行a*b,函数会返回 NaN。但是第二行代码解决了这个问题:

function multiply(a, b) {
  b = (typeof b !== 'undefined') ?  b : 1;
  return a * b;
}

multiply(5, 2); // 10
multiply(5, 1); // 5

JavaScript 算法与数据结构

index.html 被浏览器缓存

ant-design/ant-design-pro#1365

对应的nginx配置如下:

upstream example-be {
    ip_hash;
    server unix:/run/example-be.sock;
}
server{
    listen   80; #监听端口
    server_name example.com

    # 后台api
    location ~ ^/api {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        include uwsgi_params;
        uwsgi_pass example-be;
    }

    # 前端静态文件
    location ~* \.(gif|jpg|jpeg|png|css|js|ico|eot|otf|fon|font|ttf|ttc|woff|woff2)$ {
        root /var/www/example-fe/dist/;
    }

    # 前端html文件
    location / {
        # disable cache html
        add_header Cache-Control 'no-cache, must-revalidate, proxy-revalidate, max-age=0';

        root /var/www/example-fe/dist/;
        index index.html index.htm;
        try_files $uri /index.html;
    }
}

由于浏览器缓存静态文件的时间不可控,我们可以在nginx上自己配置expires 1M(1个月)

    # 前端静态文件
    location ~* \.(gif|jpg|jpeg|png|css|js|ico|eot|otf|fon|font|ttf|ttc|woff|woff2)$ {
        root /var/www/example-fe/dist/;
        expires 1M;
        add_header Cache-Control "public";
    }