前端基本功-示例代码 (一) 点这里
前端基本功-示例代码 (二) 点这里
1.一像素
伪类 + transform 完成
关于老项目,有无什么要领能兼容1px的为难问题了,个人以为伪类+transform是比较圆满的要领了。
道理是把本来元素的 border 去掉,然后运用 :before 或许 :after 重做 border ,并 transform 的 scale 减少一半,本来的元素相对定位,新做的 border 相对定位。
单条border款式设置:
.scale-1px{
position: relative;
border:none;
}
.scale-1px:after{
content: '';
position: absolute;
bottom: 0;
background: #000;
width: 100%;
height: 1px;
-webkit-transform: scaleY(0.5);
transform: scaleY(0.5);
-webkit-transform-origin: 0 0;
transform-origin: 0 0;
}
四条boder款式设置:
.scale-1px{
position: relative;
margin-bottom: 20px;
border:none;
}
.scale-1px:after{
content: '';
position: absolute;
top: 0;
left: 0;
border: 1px solid #000;
-webkit-box-sizing: border-box;
box-sizing: border-box;
width: 200%;
height: 200%;
-webkit-transform: scale(0.5);
transform: scale(0.5);
-webkit-transform-origin: left top;
transform-origin: left top;
}
最幸亏运用前也推断一下,连系 JS 代码,推断是不是 Retina 屏:
if(window.devicePixelRatio && devicePixelRatio >= 2){
document.querySelector('ul').className = 'scale-1px';
}
要领二
/*挪动端平常展现1px的问题 start*/
%border-1px{
display: block;
position:absolute;
left: 0;
width: 100%;
content: ' ';
}
.border-1px{
position: relative;
&::after{
@extend %border-1px;
bottom: 0;
border-top: 1px solid #ccc;
}
&::before{
@extend %border-1px;
top: 0;
border-bottom: 1px solid #ccc;
}
}
@media (-webkit-min-device-pixel-ratio:1.5),(min-device-pixel-ratio:1.5){
.border-1px{
&::after{
-webkit-transform: scaleY(0.7);
transform: scaleY(0.7);
}
}
}
@media (-webkit-min-device-pixel-ratio:2),(min-device-pixel-ratio:2){
.border-1px{
&::after{
-webkit-transform: scaleY(0.5);
transform: scaleY(0.5);
}
}
}
/*挪动端平常展现1px的问题 end*/
要领三
.hairline-border {
box-shadow: 0 0 0 1px;
}
@media (min-resolution: 2dppx) {
.hairline-border {
box-shadow: 0 0 0 0.5px red;
}
}
@media (min-resolution: 3dppx) {
.hairline-border {
box-shadow: 0 0 0 0.33333333px;
}
}
@media (min-resolution: 4dppx) {
.hairline-border {
box-shadow: 0 0 0 0.25px;
}
}
2.动画
animation:mymove 5s infinite;
@keyframes mymove {
from {top:0px;}
to {top:200px;}
}
js完成一个延续的动画效果
//兼容性处置惩罚
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function(callback){
window.setTimeout(callback, 1000 / 60);
};
})();
var e = document.getElementById("e");
var flag = true;
var left = 0;
function render() {
left == 0 ? flag = true : left == 100 ? flag = false : '';
flag ? e.style.left = ` ${left++}px` :
e.style.left = ` ${left--}px`;
}
(function animloop() {
render();
requestAnimFrame(animloop);
})();
3. 完成sum(2)(3)
// 写一个 function 让下面两行代码输出的效果都为 5
console.log(sum(2, 3));
console.log(sum(2)(3));
完成
function sum() {
var cache;
if (arguments.length === 1) {
cache = arguments[0];
return function ( number ) {return cache + number;};
}
else return arguments[0] + arguments[1];
};
4. 函数柯里化
函数柯里化指的是将能够吸收多个参数的函数转化为吸收单一参数的函数,而且返回吸收余下参数或效果的新函数的手艺。
函数柯里化的重要作用和特性就是参数复用、提早返回和耽误盘算/执行。
1. 参数复用
指导
// 平常函数
function add(x,y){
return x + y;
}
add(3,4); //5
// 完成了柯里化的函数
// 吸收参数,返回新函数,把参数传给新函数运用,末了求值
let add = function(x){
return function(y){
return x + y;
}
};
add(3)(4); // 7
通用的柯里化函数
觉得currying就是返回函数的函数,在此函数闭包中定义了私有域变量。
function curry(fn) {
let slice = Array.prototype.slice, // 将slice缓存起来
args = slice.call(arguments, 1); // 这里将arguments转成数组并保留
return function() {
// 将新旧的参数拼接起来
let newArgs = args.concat(slice.call(arguments));
return fn.apply(null, newArgs); // 返回执行的fn并通报最新的参数
}
}
if (typeof Function.prototype.bind === "undefined"){
Function.prototype.bind = function (thisArgs){
var fn = this,
slice = Array.prototype.slice,
args = slice.call(arguments, 1);
return function (){
let newArgs = args.concat(slice.call(arguments))
return fn.apply(thisArgs, newArgs);
}
}
}
ES6版的柯里化函数
function curry(fn, ...allArgs) {
const g = (...allArgs) => allArgs.length >= fn.length ?
fn(...allArgs) : (...args) => g(...allArgs, ...args)
return g;
}
// 测试用例
const foo = curry((a, b, c, d) => {
console.log(a, b, c, d);
});
foo(1)(2)(3)(4); // 1 2 3 4
const f = foo(1)(2)(3);
f(5); // 1 2 3 5
function trueCurrying(fn, ...args) {
if (args.length >= fn.length) {
return fn(...args)
}
return function (...args2) {
return trueCurrying(fn, ...args, ...args2)
}
}
// 比较屡次接收的参数总数与函数定义时的入参数目,
//当接收参数的数目大于或即是被 Currying 函数的传入参数数目时,
//就返回盘算效果,不然返回一个继续接收参数的函数。
//注重这点和上边的区分
问题:须要写一个函数,满足
curry(fn)(1)(2)(3) //6
var fn = function(a,b,c) {
return a+b+c;
}
function curry(fn) {
var arr = [],
mySlice = arr.slice
fnLen = fn.length;
function curring() {
arr = arr.concat(mySlice.call(arguments));
if(arr.length < fnLen) {
return curring;
}
return fn.apply(this, arr);
}
return curring;
}
curry(fn)(1)(2)(3);//6
本小题来自:几个让我印象深入的口试题(一)
2. 提早返回
var addEvent = function(el, type, fn, capture) {
if (window.addEventListener) {
el.addEventListener(type, function(e) {
fn.call(el, e);
}, capture);
} else if (window.attachEvent) {
el.attachEvent("on" + type, function(e) {
fn.call(el, e);
});
}
};
上面的要领有什么问题呢?很显然,我们每次运用addEvent为元素增添事宜的时刻,(eg. IE6/IE7)都邑走一遍if…else if …,实在只需一次剖断就能够了,怎样做?–柯里化。改成下面这模样的代码:
var addEvent = (function(){
if (window.addEventListener) {
return function(el, sType, fn, capture) {
el.addEventListener(sType, function(e) {
fn.call(el, e);
}, (capture));
};
} else if (window.attachEvent) {
return function(el, sType, fn, capture) {
el.attachEvent("on" + sType, function(e) {
fn.call(el, e);
});
};
}
})();
初始addEvent的执行实在值完成了部份的运用(只要一次的if…else if…剖断),而盈余的参数运用都是其返回函数完成的,典范的柯里化。
对照:惰性加载
let addEvent = function(ele, type, fn) {
if (window.addEventListener) {
addEvent = function(ele, type, fn) {
ele.addEventListener(type, fn, false);
}
} else if (window.attachEvent) {
addEvent = function(ele, type, fn) {
ele.attachEvent('on' + type, function() {
fn.call(ele)
});
}
}
addEvent(ele, type, fn);
3. 耽误盘算/运转
ES5中的bind要领
if (!Function.prototype.bind) {
Function.prototype.bind = function(context) {
var self = this,
args = Array.prototype.slice.call(arguments);
return function() {
return self.apply(context, args.slice(1));
}
};
}
引荐浏览:从一道口试题熟悉函数柯里化
参考文章:ES6版的柯里化函数、JS中的柯里化(currying)
5.手写一个 bind 要领
带一个参数:
Function.prototype.bind = function(context) {
let self = this,
slice = Array.prototype.slice,
args = slice.call(arguments);
return function() {
return self.apply(context, args.slice(1));
}
};
带多个参数:
//ES3完成
if(!Function.prototype.bind){
Function.prototype.bind = function(o, args){
var self = this,
boundArgs = arguments;//注:arguments是指sum.bind(null,1)中的参数null和1
return function(){ //此时返回的只是一个函数
var args = [], i;
for(var i=1; i< boundArgs.length; i++){
args.push(boundArgs[i]);
}
for(var i =0; i< arguments.length; i++){
args.push(arguments[i]);//注:这里的arguments是指result(2)中的参数2
}
return self.apply(o, args);
}
}
}
或许
// 代码来自书本 《javaScript 形式》
if (typeof Function.prototype.bind === "undefined"){
Function.prototype.bind = function (thisArgs){
var fn = this,
slice = Array.prototype.slice,
args = slice.call(arguments, 1);
return function (){
return fn.apply(thisArgs, args.concat(slice.call(arguments)));
}
}
}
//注:前后arguments不是一回事哦~
//挪用
var sum = function(x,y){ return x+y };
var result = sum.bind(null,1);
result(2); // 3
或许
Function.prototype.bind = function(){
var fn = this;
var args = Array.prototye.slice.call(arguments);
var context = args.shift();
return function(){
return fn.apply(context, args.concat(Array.prototype.slice.call(arguments)));
};
本节参考文章:js中的bind
其他文章:JavaScirpt 的 bind 函数终究做了哪些事
6.典范口试问题:new 的历程
起首来看一下,函数声明的历程
// 现实代码
function fn1() {}
// JavaScript 自动执行
fn1.protptype = {
constructor: fn1,
__proto__: Object.prototype
}
fn1.__proto__ = Function.prototype
var a = new myFunction("Li","Cherry");
//伪代码
new myFunction{
var obj = {};
obj.__proto__ = myFunction.prototype;
var result = myFunction.call(obj,"Li","Cherry");
return typeof result === 'object'? result : obj;
}
- 建立一个空对象 obj;
- 将新建立的空对象的隐式原型指向其组织函数的显现原型。
- 运用 call 转变 this 的指向
- 假如无返回值或许返回一个非对象值,则将 obj 返回作为新对象;假如返回值是一个新对象的话那末直接直接返回该对象。
所以我们能够看到,在 new 的历程当中,我们是运用 call 转变了 this 的指向。
var NEW = function(func) {
var o = Object.create(func.prototype)
var k = func.call(o)
if (typeof k === 'object') {
return k
} else {
return o
}
}
7.javascript内里的继续怎样完成,怎样防止原型链上面的对象同享
什么是原型链
当一个援用范例继续另一个援用范例的属性和要领时刻就会发作一个原型连。
ES5:寄生组合式继续:经由过程借用组织函数来继续属性和原型链来完成子继续父。
function ParentClass(name) {
this.name = name;
}
ParentClass.prototype.sayHello = function () {
console.log("I'm parent!" + this.name);
}
function SubClass(name, age) {
//如果要多个参数能够用apply 连系 ...解构
ParentClass.call(this, name);
this.age = age;
}
SubClass.prototype.sayChildHello = function (name) {
console.log("I'm child " + this.name)
}
SubClass.prototype = Object.create(ParentClass.prototype);
SubClass.prototype.constructor = SubClass;
let testA = new SubClass('CRPER')
// Object.create()的polyfill
/*
function pureObject(obj){
//定义了一个暂时组织函数
function F() {}
//将这个暂时组织函数的原型指向了传入进来的对象。
F.prototype = obj;
//返回这个组织函数的一个实例。该实例具有obj的一切属性和要领。
//因为该实例的原型是obj对象。
return new F();
}
*/
或
function subClass() {
superClass.apply(this, arguments);
this.abc = 1;
}
function inherits(subClass, superClass) {
function Inner() {}
Inner.prototype = superClass.prototype;
subClass.prototype = new Inner();
subClass.prototype.constructor = subClass;
}
inherits(subClass, superClass);
subClass.prototype.getTest = function() {
console.log("hello")
};
ES6: 实在就是ES5的语法糖,不过可读性很强..
class ParentClass {
constructor(name) {
this.name = name;
}
sayHello() {
console.log("I'm parent!" + this.name);
}
}
class SubClass extends ParentClass {
constructor(name) {
super(name);
}
sayChildHello() {
console.log("I'm child " + this.name)
}
// 从新声明父类同名要领会覆写,ES5的话就是直接操纵本身的原型链上
sayHello(){
console.log("override parent method !,I'm sayHello Method")
}
}
let testA = new SubClass('CRPER')
8.继续 JS 内置对象(Date)
写在前面,本节只记录了怎样继续Date对象…的解决计划,详细问题和剖析历程请看原文
ES5
// 须要斟酌polyfill状况
Object.setPrototypeOf = Object.setPrototypeOf ||
function(obj, proto) {
obj.__proto__ = proto;
return obj;
};
/**
* 用了点技能的继续,现实上返回的是Date对象
*/
function MyDate() {
// bind属于Function.prototype,吸收的参数是:object, param1, params2...
var dateInst = new(Function.prototype.bind.apply(Date, [Date].concat(Array.prototype.slice.call(arguments))))();
// 变动原型指向,不然没法挪用MyDate原型上的要领
// ES6计划中,这里就是[[prototype]]这个隐式原型对象,在没有规范之前就是__proto__
Object.setPrototypeOf(dateInst, MyDate.prototype);
dateInst.abc = 1;
return dateInst;
}
// 原型从新指回Date,不然基础没法算是继续
Object.setPrototypeOf(MyDate.prototype, Date.prototype);
MyDate.prototype.getTest = function getTest() {
return this.getTime();
};
let date = new MyDate();
// 平常输出,比如1515638988725
console.log(date.getTest());
ES6
class MyDate extends Date {
constructor() {
super();
this.abc = 1;
}
getTest() {
return this.getTime();
}
}
let date = new MyDate();
// 平常输出,比如1515638988725
console.log(date.getTest());
注重:这里的平常输出环境是直接用ES6运转,不经由babel打包,打包后实质上是转化成ES5的,所以效果完整不一样,会报错的
9.浅易双向数据绑定
<body>
<input type="text" id="foo">
<p id="test"></p>
<script>
var user = {}
Object.defineProperty(user, 'inputValue', {
configurable: true,
get: function() {
return document.getElementById('foo').value
},
set: function(value) {
document.getElementById('foo').value = value
document.getElementById('test').innerHTML = value
}
})
document.getElementById('foo').addEventListener('keyup', function() {
document.getElementById('test').innerHTML = user.inputValue
})
</script>
</body>
10.JavaScript完成宣布-定阅形式
宣布-定阅形式又叫观察者形式,它定义对象间的一种一对多的依靠关联,当一个对象的状况发作转变时,一切依靠于它的对象都将获得关照。JavaScript开辟中我们平常用事宜模子来替代传统的宣布-定阅形式
示例1
function Dep() {//宣布者
this.subs = [];
}
Dep.prototype.addSub = function (sub) {
this.subs.push(sub);
}
Dep.prototype.notify = function () {
this.subs.forEach(sub=>sub.update());
}
function Watcher(fn) {//定阅者
this.fn = fn;
}
Watcher.prototype.update = function () {
this.fn();
}
var dep = new Dep();
dep.addSub(new Watcher(function () {
console.log('okokok');
}))
dep.notify();
示例2
function Event(){
this.list={},
this.on=function(key,cb){//定阅事宜
if(!this.list[key]){
this.list[key] = []
}
this.list[key].push(cb)
},
this.emit = function(){//触发事宜
var key = Array.prototype.shift.call(arguments)
var e = this.list[key]
if(!e){
return
}
var args = Array.prototype.slice.call(arguments)
for(var i = 0;i<e.length;i++){
e[i].apply(null,args)
}
}
}
尝试一下:
var a = new Event()
a.on('a',function(x){console.log(x)})
a.emit('a',1)//1
引荐浏览:从单向到双向数据绑定
示例3
var myBus = (function() {
var clienlist = {},
addlisten, trigger, remove;
/**
* 增添定阅者
* @key {String} 范例
* @fn {Function} 回掉函数
* */
addlisten = function(key, fn) {
if(!clienlist[key]) {
clienlist[key] = [];
}
clienlist[key].push(fn);
};
/**
* 宣布音讯
* */
trigger = function() {
var key = [].shift.call(arguments), //掏出音讯范例
fns = clienlist[key]; //掏出该范例的对应的音讯鸠合
if(!fns || fns.length === 0) {
return false;
}
for(var i = 0, fn; fn = fns[i++];) {
fn.apply(this, arguments);
}
};
/**
* 删除定阅
* @key {String} 范例
* @fn {Function} 回掉函数
* */
remove = function(key, fn) {
var fns = clienlist[key]; //掏出该范例的对应的音讯鸠合
if(!fns) { //假如对应的key没有定阅直接返回
return false;
}
if(!fn) { //假如没有传入详细的回掉,则示意须要作废一切定阅
fns && (fns.length = 0);
} else {
for(var l = fns.length - 1; l >= 0; l--) { //遍历回掉函数列表
if(fn === fns[l]) {
fns.splice(l, 1); //删除定阅者的回掉
}
}
}
};
return {
$on: addlisten,
$emit: trigger,
$off: remove
}
})();
示例4
这个示例更像示例2、示例3的总结,我也放这里吧,多看几种写法也若干坦荡一下思绪或全当温习
卖烧饼的雇主能够把小明、小龙的电话记录下来,等店里有烧饼了在关照小龙小明来拿这就是所谓的宣布-定阅形式,代码以下:
/*烧饼店*/
var Sesamecakeshop={
clienlist:[],//缓存列表
addlisten:function(fn){//增添定阅者
this.clienlist.push(fn);
},
trigger:function(){//宣布音讯
for(var i=0,fn;fn=this.clienlist[i++];){
fn.apply(this,arguments);
}
}
}
/*小明宣布定阅*/
Sesamecakeshop.addlisten(function(price,taste){
console.log("小明宣布的"+price+"元,"+taste+"滋味的");
});
/*小龙宣布定阅*/
Sesamecakeshop.addlisten(function(price,taste){
console.log("小龙宣布的"+price+"元,"+taste+"滋味的");
});
Sesamecakeshop.trigger(10,"椒盐");
从代码中能够看出,只要小明,小龙预定了烧饼,烧饼店就能够宣布音讯通知小龙与小明。但是有个问题不知道人人发现了没有。小明只喜好椒盐滋味的。而小龙只喜好焦糖滋味的。上面的代码就满足不了客户的需求,给客户一种觉得就是,不论我喜好不喜好,你都邑发给我。假如宣布比较多,客户就会觉得讨厌,以至会想删除定阅。下边是对代码举行革新人人能够看看。
/*烧饼店*/
var Sesamecakeshop={
clienlist:{},/*缓存列表*/
/**
* 增添定阅者
* @key {String} 范例
* @fn {Function} 回掉函数
* */
addlisten:function(key,fn){
if(!this.clienlist[key]){
this.clienlist[key]=[];
}
this.clienlist[key].push(fn);
},
/**
* 宣布音讯
* */
trigger:function(){
var key=[].shift.call(arguments),//掏出音讯范例
fns=this.clienlist[key];//掏出该范例的对应的音讯鸠合
if(!fns || fns.length===0){
return false;
}
for(var i=0,fn;fn=fns[i++];){
fn.apply(this,arguments);
}
},
/**
* 删除定阅
* @key {String} 范例
* @fn {Function} 回掉函数
* */
remove:function(key,fn){
var fns=this.clienlist[key];//掏出该范例的对应的音讯鸠合
if(!fns){//假如对应的key没有定阅直接返回
return false;
}
if(!fn){//假如没有传入详细的回掉,则示意须要作废一切定阅
fns && (fns.length=0);
}else{
for(var l=fns.length-1;l>=0;l--){//遍历回掉函数列表
if(fn===fns[l]){
fns.splice(l,1);//删除定阅者的回掉
}
}
}
}
}
/*小明宣布定阅*/
Sesamecakeshop.addlisten("焦糖",fn1=function(price,taste){
console.log("小明宣布的"+price+"元,"+taste+"滋味的");
});
/*小龙宣布定阅*/
Sesamecakeshop.addlisten("椒盐",function(price,taste){
console.log("小龙宣布的"+price+"元,"+taste+"滋味的");
});
Sesamecakeshop.trigger("椒盐",10,"椒盐");
Sesamecakeshop.remove("焦糖",fn1);//注重这里是根据地点援用的。假如传入匿名函数则删除不了
Sesamecakeshop.trigger("焦糖",40,"焦糖");
引荐必读:宣布-定阅形式
11.扁平化后的数组
如:[1, [2, [ [3, 4], 5], 6]] => [1, 2, 3, 4, 5, 6]
var data = [1, [2, [ [3, 4], 5], 6]];
function flat(data, result) {
var i, d, len;
for (i = 0, len = data.length; i < len; ++i) {
d = data[i];
if (typeof d === 'number') {
result.push(d);
} else {
flat(d, result);
}
}
}
var result = [];
flat(data, result);
console.log(result);
12.冒泡排序
剖析:
- 比较相邻的两个元素,假如前一个比后一个大,则交流位置。
- 第一轮的时刻末了一个元素应该是最大的一个。
- 根据步骤一的要领举行相邻两个元素的比较,这个时刻因为末了一个元素已是最大的了,所以末了一个元素不必比较。
js代码完成
function bubble_sort(arr){
for(var i = 0;i < arr.length - 1; i++){
for(var j = 0;j < arr.length - i - 1;j++){
if(arr[j] > arr[j+1]){
[arr[j], arr[j+1]] = [arr[j + 1], arr[j]]
}
}
}
}
var arr = [3,1,5,7,2,4,9,6,10,8];
bubble_sort(arr);
console.log(arr);
13.疾速排序
疾速排序是对冒泡排序的一种革新
剖析:
- 第一趟排序时将数据分红两部份,一部份比另一部份的一切数据都要小。
- 然后递归挪用,在双方都执行疾速排序。
js代码完成
function quick_sort(arr){
if(arr.length <= 1){
return arr;
}
var pivotIndex = Math.floor(arr.length / 2);
var pivot = arr.splice(pivotIndex, 1)[0];
var left = [];
var right = [];
for (var i = 0;i < arr.length; i++) {
if(arr[i] < pivot){
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
return quick_sort(left).concat([pivot],quick_sort(right));
}
var arr=[5,6,2,1,3,8,7,1,2,3,4,7];
console.log(quick_sort(arr));
14.挑选排序
// 挑选排序:也许思绪是找到最小的放在第一位,找到第二小的放在第二位,以此类推 算法复杂度O(n^2)
挑选demo:
function selectionSort(arr) {
let len = arr.length;
let minIndex;
for (let i = 0; i < len - 1; i++) {
minIndex = i;
for (let j = i + 1; j < len; j++) {
if (arr[j] < arr[minIndex]) { //寻觅最小的数
minIndex = j; //将最小数的索引保留
}
}
[arr[i], arr[minIndex]] = [arr[minIndex], arr[i]];
}
return arr;
}
本节参考文章:2018前端口试总结…
15.插进去排序
剖析:
- 从第一个元素最先,该元素能够以为已被排序
- 掏出下一个元素,在已排序的元素序列中从后向前扫描
- 假如该元素(已排序)大于新元素,将该元素移到下一位置
- 反复步骤3,直到找到已排序的元素小于或许即是新元素的位置
- 将新元素插进去到下一位置中
- 反复步骤2
js代码完成
function insert_sort(arr){
var i=1,
j,key,len=arr.length;
for(;i<len;i++){
var j=i;
var key=arr[j];
while(--j>-1){
if(arr[j]>key){
arr[j+1]=arr[j];
}else{
break;
}
}
arr[j+1]=key;
}
return arr;
}
或
function insert_sort(arr) {
let len = arr.length;
let preIndex, current;
for (let i = 1; i < len; i++) {
preIndex = i - 1;
current = arr[i];
while (preIndex >= 0 && arr[preIndex] > current) {
arr[preIndex + 1] = arr[preIndex];
preIndex--;
}
arr[preIndex + 1] = current;
}
return arr;
}
insert_sort([2,34,54,2,5,1,7]);
华米科技 招前端
联络: 于梦中(wx:tsw0618) 内推,备注来意,简历请甩 weihongjie@huami.com