我是靠谱客的博主 稳重睫毛膏,最近开发中收集的这篇文章主要介绍前端问题总结16,项目中的难点,亮点17,react可能会问到(vue也会用到diff算法),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

1,jquery中的ajax(数据请求)

第一步:

》1:引入jquery.js 或者jquery.min.js,普通引入

<script src='jquery.js'></script>

》2: 如果是项目脚手架引入jquery(比如react),需要:

npm i jquery -S

在哪个地方使用jQuery就在什么地方引入jQuery

import $ from
'jquery'

第二步:固定格式套用请求如下(延伸概念什么是同步异步)

<script>
$.ajax({
       type: "post",
dataType: "json",
async:false,//默认设置为true,所有请求均为异步。如果需要发送同步请求,请将此选项设置为false
       url: '/Resources/GetList.ashx',
       data: {username:'zhangsan',password:'123456'},
       success: function (data) {
           if (data.code == 0 ) {
               $("#anhtml").html(data.str);
         
}
     
}
 
});
</script>

延伸作业:axios的请求(get,post有什么不同,qs的作用,如何封装调用,如何拦截等)

2,函数式继承

①:call 和apply概念区别

每个函数都包含两个非继承而来的方法:call()方法和apply()方法。

相同点:这两个方法的作用是一样的。都是在特定的作用域中调用函数,等于设置函数体内this对象的值,以扩充函数赖以运行的作用域。一般来说,this总是指向调用某个方法的对象,但是使用call()和apply()方法时,就会改变this的指向。(总结,扩展作用域,改变this指向)

不同点:

apply()方法 接收两个参数,一个是函数运行的作用域(this),另一个是参数数组。

** 语法:**apply([thisObj [,argArray] ]);,调用一个对象的一个方法,2另一个对象替换当前对象。

** 说明:**如果argArray不是一个有效数组或不是arguments对象,那么将导致一个 TypeError,如果没有提供argArray和thisObj任何一个参数,那么Global对象将用作thisObj。

call()方法 第一个参数和apply()方法的一样,但是传递给函数的参数必须列举出来。

** 语法:**call(thisObj, arg1, arg2, arg3, arg4);,应用某一对象的一个方法,用另一个对象替换当前对象。

** 说明:** call方法可以用来代替另一个对象调用一个方法,call方法可以将一个函数的对象上下文从初始的上 下文改变为thisObj指定的新对象,如果没有提供thisObj参数,那么Global对象被用于thisObj。

1》call()方法使用示例:

//例1
   <script>
       window.color = 'red';
       document.color = 'yellow';
       var s1 = {color: 'blue' };
       
       function changeColor(){
           console.log(this.color);
     
}
​
       changeColor.call();         //red (默认传递参数)
       changeColor.call(window);   //red
       changeColor.call(document); //yellow
       changeColor.call(this);     //red
       changeColor.call(s1);       //blue
​
   </script>
​
   //例2
   var Pet = {
       words : '...',
       speak : function (say) {
           console.log(say + ''+ this.words)
     
}
 
}
   Pet.speak('Speak'); // 结果:Speak...
​
   var Dog = {
       words:'Wang'
 
}
​
   //将this的指向改变成了Dog
   Pet.speak.call(Dog, 'Speak'); //结果: SpeakWang

2》apply示例:

//例1
   <script>
       window.number = 'one';
       document.number = 'two';
​
       var s1 = {number: 'three' };
       function changeColor(){
           console.log(this.number);
     
}
​
       changeColor.apply();         //one (默认传参)
       changeColor.apply(window);   //one
       changeColor.apply(document); //two
       changeColor.apply(this);     //one
       changeColor.apply(s1);       //three
​
   </script>
​
   //例2
   function Pet(words){
       this.words = words;
       this.speak = function () {
           console.log( this.words)
     
}
 
}
   function Dog(words){
       //Pet.call(this, words); //结果: Wang
      Pet.apply(this, arguments); //结果: Wang
 
}
   var dog = new Dog('Wang');
   dog.speak();

3》不同点示例:

function add(c,d){
       return this.a + this.b + c + d;
 
}
​
   var s = {a:1, b:2};
   console.log(add.call(s,3,4)); // 1+2+3+4 = 10
   console.log(add.apply(s,[5,6])); // 1+2+5+6 = 14

②:ES5 构造函数继承

function Parent() {
 this.name = 'parent';
 this.colors = ['black', 'yellow', 'red']
}
function Child() {
 Parent.call(this);
 this.type = 'child';
}
Parent.prototype.age = 12;
Parent.prototype.say = function(){
 console.log('hello');
}
var q1 = new Child();
console.log(q1.name);  // parent
console.log(q1.colors);// [ 'black', 'yellow', 'red' ]
console.log(q1);
  //Child {name: "parent", colors: Array(3), type: "child"}
console.log(q1.age);   // undefined
console.log(q1.say()); // 报错
TypeError: q1.say is not a function

综上总结:Child无法继承Parent的原型对象,并没有真正的实现继承(部分继承)

③:ES5 原型链继承

function Parent() {
 this.name   = 'parent';
 this.colors = ['black', 'yellow', 'red']
}
function Child() {
 this.type = 'child';
}
Parent.prototype.age = 12;
Parent.prototype.say = function(){
 console.log('hello');
}
Child.prototype = new Parent();
var q1 = new Child();
console.log(q1);
console.log(q1.age); // 12
console.log(q1.say()); // hello undefined(因为方法没返回))
var q2 = new Child();//具有相同的原型,所以不分彼此
q1.colors.push('pink');
console.log(q1.colors) // [ 'black', 'yellow', 'red', 'pink' ]
console.log(q2.colors) // [ 'black', 'yellow', 'red', 'pink' ]

综上:被new出来的实例不能隔离(因为具有相同的原型,所以没有独立性,一个被改其他的跟着被改动)

④:组合继承(构造函数和原型链继承)

function Parent() {
 this.name = 'parent';
 this.colors = ['black', 'yellow', 'red']
}
function Child() {
 Parent.call(this);
 this.type = 'child';
}
Parent.prototype.age = 12;
Parent.prototype.say = function(){
 console.log('hello');
}
//Child.prototype = new Parent();//也可以用这种方式实现,但是父类的构造函数被执行了两次,
//第一次是Child.prototype = new Parent(),
//第二次是在实例化的时候,这是没有必要的。(优化如下)
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
​
var q1 = new Child();
var q2 = new Child();
q1.colors.push('pink');
console.log(q1.colors) // [ 'black', 'yellow', 'red','pink']
console.log(q2.colors) // [ 'black', 'yellow', 'red', ]

综上:Object.create是一种创建对象的方式,它会创建一个中间对象(或者叫寄生组合继承)

⑤:ES6 class继承

// es6的继承
class Parent {
constructor(name='dfd') {
this.name = name;
}
say() {
console.log('parent has say method. ' + this.name);
}
}
class Child extends Parent {
constructor(name) {
super(name);
}
fly() {
console.log('chid can fly...');
}
}
var p = new Child('tom');
p.say();//parent has say method. tom
p.fly();//chid can fly...

3,this指向问题

参考链接:this - JavaScript | MDN

1》全局环境:

无论是否在严格模式下,在全局执行环境中(在任何函数体外部)this 都指向全局对象。

// 在浏览器中, window 对象同时也是全局对象:
console.log(this === window); // true
a = 37;
console.log(window.a); // 37
this.b = "MDN";
console.log(window.b)
// "MDN"
console.log(b)
// "MDN"

2》函数(运行内)环境

在函数内部,this的值取决于函数被调用的方式。

情况<1>:普通函数

因为下面的代码不在严格模式下,且 this 的值不是由该调用设置的,所以 this 的值默认指向全局对象

function f1(){
return this;
}
//在浏览器中:
f1() === window;
//在浏览器中,全局对象是window
//在Node中:
f1() === global;

情况<2>:严格模式

严格模式下,this将保持他进入执行环境时的值,所以下面的this将会默认为undefined`。

function f2(){
"use strict"; // 这里是严格模式
return this;
}
f2() === undefined; // true

所以,在严格模式下,如果 this 没有被执行环境(execution context)定义,那它将保持为 undefined

注释:

<!--在第二个例子中,`this`的确应该是[undefined](https://developer.mozilla.org/zh-CN/docs/Glossary/undefined),因为`f2`是被直接调用的,而不是作为对象的属性或方法调用的(如 `window.f2()`)。有一些浏览器最初在支持[严格模式](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Strict_mode)时没有正确实现这个功能,于是它们错误地返回了`window`对象。-->

情况<3>:继承改变this指向(call,apply)

如果要想把 this 的值从一个环境传到另一个,就要用 call 或者apply 方法。

// 将一个对象作为call和apply的第一个参数,this会被绑定到这个对象。
var obj = {a: 'Custom'};
// 这个属性是在global对象定义的。
var a = 'Global';
function whatsThis(arg) {
return this.a;
// this的值取决于函数的调用方式
}
whatsThis();
// 'Global'
whatsThis.call(obj);
// 'Custom'
whatsThis.apply(obj); // 'Custom'

当一个函数在其主体中使用 this 关键字时,可以通过使用函数继承自Function.prototypecallapply 方法将 this 值绑定到调用中的特定对象。

3》bind方法

ECMAScript 5 引入了 Function.prototype.bind。调用f.bind(someObject)会创建一个与f具有相同函数体和作用域的函数,但是在这个新函数中,this将永久地被绑定到了bind的第一个参数,无论这个函数是如何被调用的。

function f(){
return this.a;
}
var g = f.bind({a:"azerty"});
console.log(g()); // azerty
var h = g.bind({a:'yoo'}); // bind只生效一次!
console.log(h()); // azerty
var o = {a:37, f:f, g:g, h:h};
console.log(o.f(), o.g(), o.h()); // 37, azerty, azerty

4》箭头函数

在箭头函数中,箭头函数的this被设置为封闭的词法环境的,this与封闭词法环境的this保持一致,换句话说,箭头函数中的this取决于该函数被创建时的环境。在全局代码中,它将被设置为全局对象,

var objProject = this;
var foo = (() => this);
console.log(foo());
// window
console.log(objProject);
// window
console.log(foo() === objProject ); // true
// 作为对象的一个方法调用
var obj = {foo: foo};
console.log(obj.foo() === objProject ); // true
// 尝试使用call来设定this
console.log(foo.call(obj) === objProject ); // true
// 尝试使用bind来设定this
foo = foo.bind(obj);
console.log(foo() === objProject ); // true

5》作为对象的方法

当函数作为对象里的方法被调用时,它们的 this 是调用该函数的对象。

下面的例子中,当 o.f()被调用时,函数内的this将绑定到o对象。

var o = {
prop: 37,
f: function() {
return this.prop;
}
};
console.log(o.f()); // logs 37

请注意,这样的行为,根本不受函数定义方式或位置的影响。在前面的例子中,我们在定义对象o的同时,将函数内联定义为成员 f 。但是,我们也可以先定义函数,然后再将其附属到o.f。这样做会导致相同的行为:

var o = {prop: 37};
function independent() {
return this.prop;
}
o.f = independent;
console.log(o.f()); // logs 37

综上:这表明函数是从of成员调用的才是重点

同样,this 的绑定只受最靠近的成员引用的影响。在下面的这个例子中,我们把一个方法g当作对象o.b的函数调用。在这次执行期间,函数中的this将指向o.b。事实证明,这与他是对象 o 的成员没有多大关系,最靠近的引用才是最重要的。

o.b = {g: independent, prop: 42};
console.log(o.b.g()); // 42

6》作为构造函数

当一个函数用作构造函数时(使用new关键字),它的this被绑定到正在构造的新对象。

虽然构造器返回的默认值是this所指的那个对象,但它仍可以手动返回其他的对象(如果返回值不是一个对象,则返回this对象)

/*
* 构造函数这样工作:
*
* function MyConstructor(){
*
// 函数实体写在这里
*
// 根据需要在this上创建属性,然后赋值给它们,比如:
*
this.fum = "nom";
*
// 等等...
*
*
// 如果函数具有返回对象的return语句,
*
// 则该对象将是 new 表达式的结果。
*
// 否则,表达式的结果是当前绑定到 this 的对象。
*
//(即通常看到的常见情况)。
* }
*/
function C(){
this.a = 37;
}
var o = new C();
console.log(o.a); // logs 37
function C2(){
this.a = 37;
return {a:38};
}
o = new C2();
console.log(o.a); // logs 38

7》作为DOM事件处理函数

当函数被用作事件处理函数时,它的this指向触发事件的元素(一些浏览器在使用非addEventListener的函数动态添加监听函数时不遵守这个约定)。

// 被调用时,将关联的元素变成蓝色
function bluify(e){
console.log(this === e.currentTarget); // 总是 true
// 当 currentTarget 和 target 是同一个对象时为 true
console.log(this === e.target);
this.style.backgroundColor = '#A5D9F3';
}
// 获取文档中的所有元素的列表
var elements = document.getElementsByTagName('*');
// 将bluify作为元素的点击监听函数,当元素被点击时,就会变成蓝色
for(var i=0 ; i<elements.length ; i++){
elements[i].addEventListener('click', bluify, false);
}

关于this问题的一道面试题:

var baz = 0;
let foo = {
bar:function() {
console.log(this,this.baz);
return this.baz;
},
baz:1
};
let foo2 = {
baz:2
};
let a = foo.bar();
//作为对象的方法调用,this指向调用函数的对象,即foo
let b = foo.bar.call(foo2); //使用call方法将this绑定到第一个参数对象向,此时,this指向foo2
let fn = foo.bar;
let c = fn(); //let fn创建的对象,此时,fn = function(){...},此时函数执行时,默认指向全局window对象
let d;
(function test(){
d = arguments[0]()
})(foo.bar);
// arguments.callee包括了一个函数的引用去创建一个arguments对象,它能让一个匿名函数很方便的指向本身,即此时this指向arguments类数组对象
console.log(a,b,c,d);

4,setTimeout ,执行函数

JavaScript
是单线程执行的,也就是无法同时执行多段代码,当某一段代码正在执行的时候,所有后续的任务都必须等待,形成一个队列,一旦当前任务执行完毕,再从队列中取出下一个任务。这也常被称为
“阻塞式执行”。所以一次鼠标点击,或是计时器到达时间点,或是 Ajax
请求完成触发了回调函数,这些事件处理程序或回调函数都不会立即运行,而是立即排队,一旦线程有空闲就执行。假如当前 JavaScript
进程正在执行一段很耗时的代码,此时发生了一次鼠标点击,那么事件处理程序就被阻塞,用户也无法立即看到反馈,事件处理程序会被放入任务队列,直到前面的代码结束以后才会开始执行。如果代码中设定了一个
`setTimeout`,那么浏览器便会在合适的时间,将代码插入任务队列,如果这个时间设为 0,就代表立即插入队列,但不是立即执行,仍然要等待前面代码执行完毕。所以 `setTimeout` 并不能保证执行的时间,是否及时执行取决于 JavaScript 线程是拥挤还是空闲。
例如:
<script>
console.log("1");
setTimeout( function() {
console.log("Hello World");
}, 0) //延时0秒
console.log("2");
</script>

5,自执行函数(形成作用域)

(function fun(){})();//称为自执行函数或立即执行函数
// 方式一
(function fun4(){
console.log("fun4");
}()); // "fun4"

分析:因为在JavaScript语言中,()里面不能包含语句(只能是表达式),所以解析器在解析到function关键字的时候,会把它们当作function表达式,而不是正常的函数声明。 除了上面直接整个包含住,也可以只包含住函数体(方式二),如下:

// 方式二
(function fun5(){
console.log("fun5");
})();// "fun5"

注:写法上建议采用方式一

“歪瓜裂枣”的自执行函数

除了上面()小括弧可以把function关键字作为函数声明的含义转换成函数表达式外,JavaScript的&& 与操作、||或操作、,逗号等操作符也有这个效果。

true && function () { console.log("true &&") } (); // "true &&"
false || function () { console.log("true ||") } (); // "true ||"
0, function () { console.log("0,") } (); // "0,"
// 此处要注意: &&, || 的短路效应。即: false && (表达式1)
是不会触发表达式1;
// 同理,true || (表达式2) 不会触发表达式2

如果不在意返回值,也不在意代码的可读性,我们甚至还可以使用一元操作符(! ~ - + ),函数同样也会立即执行。

!function () { console.log("!"); } (); //"!"
~function () { console.log("~"); } (); //"~"
-function () { console.log("-"); } (); //"-"
+function () { console.log("+"); } (); //"+"

甚至还可以使用new关键字:

// 注意:采用new方式,可以不要再解释花括弧 `}` 后面加小括弧 `()`
new function () { console.log("new"); } //"new"
// 如果需要传递参数
new function (a) { console.log(a); } ("newwwwwwww"); //"newwwwwwww"

嗯,最好玩的是赋值符号=同样也有此效用(例子中的i变量方式):

//此处 要注意区分 i 和 j 不同之处。前者是函数自执行后返回值给 i ;后者是声明一个函数,函数名为 j 。
var i = function () { console.log("output i:"); return 10; } (); // "output i:"
var j = function () { console.log("output j:"); return 99;}
console.log(i); // 10
console.log(j); // ƒ () { console.log("output j:"); return 99;}

上面提及到,要注意区分 var ivar j 不同之处(前者是函数自执行后返回值给i ;后者是声明一个函数,函数名为j)。如果是看代码,我们需要查看代码结尾是否有没有()才能区分。一般为了方便开发人员阅读,我们会采用下面这种方式:

var i2 = (function () { console.log("output i2:"); return 10; } ()); // "output i2:"
var i3 = (function () { console.log("output i3:"); return 10; }) (); // "output i3:"
// 以上两种都可以,但依旧建议采用第一种 i2 的方式。(个人依旧喜欢第二种i3方式)

自执行函数应用

for( var i=0;i<3;i++){
setTimeout(function(){
console.log(i);
}
,300);
}
// 输出结果 3,3,3

如果想输出1,2,3,可以用自执行函数如下:

for(var i=1;i<=3;i++){
(function(index){
setTimeout(function(){
console.log(index)
},0)
}(i))
}

分析:尽管循环执行结束,i值已经变成了3。但因遇到了自执行函数,当时的i值已经被 lockedIndex锁住了。也可以理解为 自执行函数属于for循环一部分,每次遍历i,自执行函数也会立即执行。所以尽管有延时器,但依旧会保留住立即执行时的i

for( var i=0;i<3;i++){
setTimeout((function(lockedInIndex){
console.log(lockedInIndex);
})(i)
,300);
}

6,堆栈的概念=》地址指针

js里实现队列与堆栈 - Joans - 博客园

这里先说两个概念:**1、堆(heap)2、栈(stack)

**堆 是堆内存的简称,栈 是栈内存的简称

说到堆栈,我们讲的就是内存的使用和分配了,没有寄存器的事,也没有硬盘的事。 各种语言在处理堆栈的原理上都大同小异。堆是动态分配内存,内存大小不一,也不会自动释放。栈是自动分配相对固定大小的内存空间,并由系统自动释放。

javascript的基本类型就5种:Undefined、Null、Boolean、Number和String,它们都是直接按值存储在栈中的,每种类型的数据占用的内存空间的大小是确定的,并由系统自动分配和自动释放。这样带来的好处就是,内存可以及时得到回收,相对于堆来说,更加容易管理内存空间。

javascript中其他类型的数据被称为引用类型的数据 : 如对象(Object)、数组(Array)、函数(Function) …,它们是通过拷贝和new出来的,这样的数据存储于堆中。其实,说存储于堆中,也不太准确,因为,引用类型的数据的地址指针是存储于栈中的,当我们想要访问引用类型的值的时候,需要先从栈中获得对象的地址指针,然后,在通过地址指针找到堆中的所需要的数据。

说来也是形象,栈,线性结构,后进先出,便于管理。堆,一个混沌,杂乱无章,方便存储和开辟内存空间

例如:

	var arr1 = [1,2,5,8];
var arr2 = arr1 ;
var str1 = arr1[2];
console.log(arr2);//1,2,5,8
console.log(str1);//5
arr2[4] = 99;
str1 = 6;
console.log(arr1);//1,2,5,8,99
console.log(arr1[2]);//5

上方例子得知,当我改变arr2中的数据时,arr1中数据也发生了变化,当改变str1的数据值时,arr1却没有发生改变。为什么?这就是传值与传址的区别。

<!--因为arr1是数组,属于引用类型,所以它赋予给arr2的时候传的是栈中的地址(相当于新建了一个不同名“指针”),而不是堆内存中的对象的值。str1得到的是一个基本类型的赋值,因此,str1仅仅是从arr1堆内存中获取了一个数值,并直接保存在栈中。arr1、arr2都指向同一块堆内存,arr2修改的堆内存的时候,也就会影响到arr1,str1是直接在栈中修改,并且不能影响到arr1堆内存中的数据。-->

堆(heap)、栈(stack)

浅拷贝和深拷贝

上边说到的赋值方式就是浅拷贝,那么什么叫作深拷贝呢?就是要将arr1的每个基本类型的数据都遍历一遍,依次的赋值给arr2的对应字段。避免产生因为地址引用带来的问题(或者 [...arr2])

var arr1 = [1,2,5,8];
var arr2 = [];
for(var i=0;i<arr1.length;i++){
arr2[i]=arr1[i];
};
console.log(arr2)//1,2,5,8
arr2[4]=99;
console.log(arr2)//1,2,5,8,99
console.log(arr1)//1,2,5,8
//javascript面向对象的语言本身在处理对象和非对象上就进行了划分,从数据结构的角度来讲,对象就是栈的指针和堆中的数值。

7,dom二级事件

一. 0级DOM分为两种

  1. 行内事件:在标签中写事件

  2. 元素.on事件名=函数

1.行内
<input type="button" id="btn" value="按钮" οnclick="alert('上海尚学堂,你值得拥有!')">

2.元素.on事件名=函数
document.getElementById("btn").onclick = function () {
alert('上海尚学堂,你值得拥有!');
}

为什么没有1级DOM?
DOM级别1于1998年10月1日成为W3C推荐标准。1级DOM标准中并没有定义事件相关的内容,所以没有所谓的1级DOM事件模型。在2级DOM中除了定义了一些DOM相关的操作之外还定义了一个事件模型 ,这个标准下的事件模型就是我们所说的2级DOM事件模型 

三, 2级Dom事件

概念:Dom二级事件可以解决事件冒泡以及一个对象只能绑定一个事件的问题(主要)

只有一个:监听方法,有两个方法用来添加和移除事件处理程序:addEventListener()和removeEventListener()。
它们都有三个参数:第一个参数是事件名(如click);
         第二个参数是事件处理程序函数;
        
第三个参数如果是true则表示在捕获阶段调用,为false表示在冒泡阶段调用。
addEventListener():可以为元素添加多个事件处理程序,触发时会按照添加顺序依次调用。
removeEventListener():不能移除匿名添加的函数。
例:
document.getElementById("btn").addEventListener("click", function(){alert('上海尚学堂,你值得拥有!')}, false);
 <input id='input' type="text" οnclick="alert('haha')" />
   <script>
       document.getElementById('input').onclick = function() {
           alert('fff')
     
};
       document.getElementById('input').addEventListener('click', function() {
           alert('mmm')
     
}, true)
       document.getElementById('input').addEventListener('click', function() {
           alert('ooooo')
     
}, true)
       document.getElementById('input').addEventListener('click', function() {
           alert('pppp')
     
}, false)
​
       // 兼容低版本IE的写法
       document.getElementById('input').attachEvent("onclick", function() {
           alert('pppp')
     
});
   </script>
​
// mmm ooooo fff pppp
//只有2级DOM包含3个事件:事件捕获阶段、处于目标阶段和事件冒泡阶段
​
//dom2不会覆盖,会依次执行,dom1和dom2是可以共存的

但是在IE6-8浏览器中,不支持addEventListener,如果想实现DOM2事件绑定只能用attachEvent/detachEvent

它只有两个参数,不能像addEventListener那样控制在哪个阶段发生,默认只能在冒泡阶段发生,同时行为需要添加on(和DOM0特别的类似)

box.attachEvent('onclick',fn1)

8,SEO优化

搜索引擎优化 (SEO) 是提高一个网站在搜索引擎中的排名(能见度)的过程。如果网站能够在搜索引擎中有良好的排名,有助于网站获得更多的流量。

SEO主要研究搜索引擎是工作的原理,是什么人搜索,输入什么搜索关键字)。优化一个网站,可能涉及内容的编辑,增加关键字的相关性。推广一个网站,可以增加网站的外链数量。

搜索引擎优化需要修改一个网站的HTML源代码和网站内容。搜索引擎优化策略应在网站建设之前就纳入网站的发展,尤其是网站的菜单和导航结构。

笼统的说,所有使用作弊手段或可疑手段的,都可以称为黑帽SEO。比如说垃圾链接,隐藏网页,桥页,关键词堆砌等等。

黑帽seo就是作弊的意思,黑帽seo手法不符合主流搜索引擎发行方针规定。黑帽SEO获利主要的特点就是短平快,为了短期内的利益而采用的作弊方法。同时随时因为搜索引擎算法的改变而面临惩罚。

不论是白帽seo还是黑帽seo没有一个精准的定义。笼统来说所有使用作弊手段或一些可疑手段的都可称为黑帽SEO。例如隐藏网页,关键词堆砌,垃圾链接,桥页等等。

优化:

  搜索引擎优化
​
  一、内部优化
  (1)META标签优化:例如:TITLE,KEYWORDS,DESCRIPTION等的优化;
  (2)内部链接的优化,包括相关性链接(Tag标签),锚文本链接,各导航链接,及图片链接;
  (3)网站内容更新:每天保持站内的更新(主要是文章的更新等)。
  (4)在网站上合理设置Robot.txt文件;
  二、外部优化
  (1)外部链接类别:友情链接、博客、论坛、B2B、新闻、分类信息、贴吧、知道、百科、站群、相关信息网等尽量保持链接的多样性;
  (2)外链运营:每天添加一定数量的外部链接,使关键词排名稳定提升;
  (3)外链选择:与一些和你网站相关性比较高,整体质量比较好的网站交换友情链接,巩固稳定关键词排名。

 (2) 网页代码优化   

	1.<title>标题:只强调重点即可,尽量把重要的关键词放在前面,关键词不要重复出现,尽量做到每个页面的<title>标题中不要设置相同的内容。
  2.<meta keywords>标签:关键词,列举出几个页面的重要关键字即可,切记过分堆砌。
  3.<meta description>标签:网页描述,需要高度概括网页内容,切记不能太长,过分堆砌关键词,每个页面也要有所不同。
  4.<body>中的标签:尽量让代码语义化,在适当的位置使用适当的标签,用正确的标签做正确的事。让阅读源码者和“蜘蛛”都一目了然。比如:h1-h6是用于标题类的,<nav>标签是用来设置页面主导航的等。
  5.<a>标签:页内链接,要加“title” 属性加以说明,让访客和 “蜘蛛” 知道。而外部链接,链接到其他网站的,则需要加上el="nofollow"属性, 告诉 “蜘蛛” 不要爬,因为一旦“蜘蛛”爬了外部链接之后,就不会再回来了。
  6.正文标题要用<h1>标签:“蜘蛛” 认为它最重要,若不喜欢<h1>的默认样式可以通过CSS设置。尽量做到正文标题用<h1>标签,副标题用<h2>标签, 而其它地方不应该随便乱用 h 标题标签。
  7.<br>标签:只用于文本内容的换行,比如:
<p>
  
第一行文字内容<br/>
第二行文字内容<br/>
第三行文字内容
</p>
 8.表格应该使用<caption>表格标题标签
 9.<img>应使用 “alt” 属性加以说明
 10.<strong>、<em>标签 : 需要强调时使用。<strong>标签在搜索引擎中能够得到高度的重视,它能突出关键词,表现重要的内容,<em>标签强调效果仅次于<strong>标签。
    <b>、<i>标签: 只是用于显示效果时使用,在SEO中不会起任何效果。
 11、文本缩进不要使用特殊符号&nbsp; 应当使用CSS进行设置。版权符号不要使用特殊符号 &copy; 可以直接使用输入法,拼“banquan”,选择序号5就能打出版权符号©。
 12、巧妙利用CSS布局,将重要内容的HTML代码放在最前面,最前面的内容被认为是最重要的,优先让“蜘蛛”读取,进行内容关键词抓取。
 13.重要内容不要用JS输出,因为“蜘蛛”不认识
 14.尽量少使用iframe框架,因为“蜘蛛”一般不会读取其中的内容
 15.谨慎使用display:none :对于不想显示的文字内容,应当设置z-index或设置到浏览器显示器之外。因为搜索引擎会过滤掉display:none其中的内容。
16. 不断精简代码
 17.js代码如果是操作DOM操作,应尽量放在body结束标签之前,html代码之后。

注:vue 不建议做seo,如果要做可以考虑->Vue的通用框架Nuxt.js

基于Vue的通用框架Nuxt.js - 肖秋雄 - 博客园

9,工厂模式

个人理解:抽离出有规律的对象,然后传不同的参数去调用一个对象返回不同的结果

从ES6重新认识JavaScript设计模式(二): 工厂模式 - 简书

所有的设计模式:

JS设计模式 - 初心不负 - 博客园

10,ES6 数组语法,数组去重等(es6,原生)

Array.from概念:从一个类似数组或可迭代对象创建一个新的,浅拷贝的数组实例

实际上:将一个类数组对象或者可遍历对象转换成一个真正的数组。

  那么什么是类数组对象呢?所谓类数组对象,最基本的要求就是具有length属性的对象

情况1:(要有length属性)

var obj = {
0:20,
1:'华清',
'length': 2
}
array = Array.from(obj)// [20, "华清"]
var obj = {
0:20,
1:'华清',
}
array = Array.from(obj)//[] 说明需要有length属性
console.log(array)

情况2:将Set结构的数据转换为真正的数组:

let arr = [12,45,97,9797,564,134,45642]
let set = new Set(arr)
console.log(set)
console.log(Array.from(set))
// [ 12, 45, 97, 9797, 564, 134, 45642 ]

情况3:

let
str = 'hello world!';
console.log(Array.from(str)) // ["h", "e", "l", "l", "o", " ", "w", "o", "r", "l", "d", "!"]

Es6 新特性:Set

特性

似于数组,但它的一大特性就是所有元素都是唯一的,没有重复。

我们可以利用这一唯一特性进行数组的去重工作。

let set6 = new Set([1, 2, 2, 3, 4, 3, 5])
console.log('distinct 1:', set6)//distinct 1: Set(5) {1, 2, 3, 4, 5}

1.向Set中添加元素。

let set1 = new Set()
set1.add(1)
set1.add(2)
set1.add(3)
console.log('added:', set1)

2.从Set中删除元素。

let set1 = new Set()
set1.add(1)
set1.add(2)
set1.add(3)
set1.delete(1)
console.log('deleted:', set1)

3.判断某元素是否存在。

let set1 = new Set()
set1.add(1)
set1.add(2)
set1.add(3)
set1.delete(1)
console.log('has(1):', set1.has(1))
console.log('has(2):', set1.has(2))

JavaScript 高性能数组去重 - Wise.Wrong - 博客园(效率比较)

方法一:(for循环双层循环,占用内存高,效率最低)(15w数据大概20152ms)

let arr = [1, 2, 3, 4, 2, 1];
for (let i=0, len=arr.length; i<len; i++) {
for (let j=i+1; j<len; j++) {
if (arr[i] == arr[j]) {
arr.splice(j, 1);
// splice 会改变数组长度,所以要将数组长度 len 和下标 j 减一
len--;
j--;
}
}
}
console.log(arr)
// [1, 2, 3,4]

方法二:(filter+indexOf)(15w数据大概8427ms)

let arr = [1, 2, 3, 4, 2, 1];
var aaa =
arr.filter((item, index)=> {
return arr.indexOf(item) === index
})
console.log(aaa)
// [1, 2, 3,4]

方法三:(Array.from + Set)(15w数据大概57ms)(150w数据大概需要678ms)

let arr = [1, 2, 3, 2, 1];
function unique(arr){
return Array.from(new Set(arr));
}
console.log(unique(arr))
// [1, 2, 3]

方法四:(拓展运算+set)

let arr = [1, 2, 3, 2, 1];
function unique(arr){
return [...new Set(arr)];
}
console.log(unique(arr))
// [1, 2, 3]

方法五:(用sort先排序,然后对比相近的数据是否相等)

这种方法只做了一次排序和一次循环,所以效率会比较高(15w数据大概120ms)

let arr = [1, 2, 3, 4, 2, 1];
arr = arr.sort()
let result = [arr[0]]
for (let i=1, len=arr.length; i<len; i++) {
arr[i] !== arr[i-1] && result.push(arr[i])
}
console.log(result)
// [1, 2, 3,4]

方法六:(创建一个空对象,然后用 for 循环遍历利用对象的属性不会重复这一特性,校验数组元素是否重复)

效率超级高:(15w数据大概16ms)(150w数据大概93ms)简直666666666

let arr
= [1, 2, 3, 4,2, 2, 1];
let result = []
let obj
= {}
for (let i of arr) {
if (!obj[i]) {
result.push(i)
obj[i] = 1
}
}
console.log(result)
// [1, 2, 3,4]

11,原型,继承

一、对象等级划分

  我们认为在javascript任何值或变量都是对象,但是我还需要将javascript中的对象分为一下几个等级。

  首先Object是顶级公民,这个应该毋庸置疑,因为所有的对象都是间接或直接的通过它衍生的。Function是一等公民,下面会做解释。几个像String,Array,Date,Number,Boolean,Math等内建对象是二等公民。剩下的则都是低级公民。

1.构造函数

构造函数 ,是一种特殊的方法。主要用来在创建对象时初始化对象。每个构造函数都有prototype(原型)属性

原型(prototype)

  概念:每一个构造函数都有一个prototype属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承。

function Person() {
}
// 虽然写在注释里,但是你要注意:
// prototype是函数才会有的属性
Person.prototype.name = 'Kevin';
var person1 = new Person();
var person2 = new Person();
console.log(person1.name) // Kevin
console.log(person2.name) // Kevin

proto

每一个JavaScript对象(除了 null )都具有的一个属性,叫proto,这个属性会指向该对象的原型proto是一个指针

function Person() {
}
var person = new Person();
console.log(person.__proto__ === Person.prototype); // true

constructor

每个原型都有一个 constructor 属性指向关联的构造函数 实例原型指向构造函数

function Person() {
}
console.log(Person === Person.prototype.constructor); // true

原型链

  概念:在javascript中,每个对象都有一个指向它的原型(prototype)对象的内部链接。每个原型对象又有自己的原型,直到某个对象的原型为null为止,组成这条链的最后一环

个人理解:根据proto一直向上查找,直到找到null为止

构造/new调用函数的时候做了什么**:

  1. 创建一个全新的对象。

  2. 这个新对象的原型(Object.getPrototypeOf(target))指向构造函数的prototype对象。

  3. 该函数的this会绑定在新创建的对象上。

  4. 如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象。

  5. 我们称这个新对象为构造函数的实例。

12,vue中key的作用

diff算法,使用key来给每个节点做一个唯一标识,Diff算法就可以正确的识别此节点,找到正确的位置区插入新的节点。

所以一句话,key的作用主要是为了高效的更新虚拟DOM

另外vue中在使用相同标签名元素的过渡切换时,也会使用到key属性,其目的也是为了让vue可以区分它们,

否则vue只会替换其内部属性而不会触发过渡效果。

13,如何进行组件复用

已经存在的ui类库,elementui 等等···就是灵活的组件

[一个好的组件库,衡量标准主要包括灵活性、复用性、全面性。灵活性指一个组件的字段、icon、配色都应该可以灵活改写,以应对多样化的需求。复用性指对于通用组件,应当是可以在不同项目间复用的。全面性则指一套组件库应当覆盖尽可能多的常用元素。 组件化的初衷并非提高一致性、利于团队合作这些高大上的目的,而是人类进步的第一生产力——「懒」。消灭重复劳动是提高效率的主要途径,勤劳如小蜜蜂的设计师也不例外,那么组件化就是无论交互还是UI设计中我们不得不思考的问题。]

[2、复用性:组件库的意义

上面说到组件化是源于「懒」,并不是说组件化的作用仅限于简化重复操作。除了提高设计师个人的设计效率这一显而易见的好处之外,在交互设计阶段对常见的元素控件进行组件化,还有很多更深层次的意义: 一致性「从娃娃抓起」:从交互稿阶段开始,就让整个项目的产出物具有高度的一致性,使用同一组件库画的每个顶栏、每个列表、每个弹框都是遵循同一规则的。 与视觉设计无缝衔接:Sketch为交互设计和视觉设计阶段的无缝衔接提供了最好的平台,交互组件库可以直接交付视觉设计师进行视觉设计,形成真正的UI组件库(UI Kit)。 有助于形成设计规范:当UI组件库形成后,「设计规范的积淀」——这个贯穿交互和UI设计管理工作中的老大难问题,就已经解决了一大半了,经过评审确认的UI组件库可以直接作为视觉设计规范的一部分。 利于团队合作:无论是交互设计组件库还是UI组件库,经过整理和完善,在项目或者团队中推行开来,对项目内,或者不同项目间设计成果的一致性、合作效率都大有裨益,也有助于让整个品牌形成有效的辨识度。 ]

[既然组件化有这么多的好处,哪些元素可以进行组件化呢?其实很简单,凡是你觉得已经重复画了很多次的元素,不同场景下都会用到的元素,都可以考虑将其单独拿出来进行组件化。]

《《《《《命名》》》》》》

组件的命名应该跟业务无关。应该依据组件的功能为组件命名。

例如,一个展示公司部门的列表,把每一项作为一个组件,并命名为 bumen。这时,有一个需求要展示团队人员列表,样式跟刚刚的部门列表一样。显然,bumen这个名字就不适合了。(list)

因此,可复用组件在命名上应避免跟业务扯上关系,以组件的角色、功能对其命名。Item、ListItem、Cell。可以参考 Bootstrap、ElementUI 等一些 UI 框架的命名。

《《《《业务数据无关》》》》

可复用组件只负责 UI 上的展示和一些交互以及动画,如何获取数据跟它无关,因此不要在组件内部去获取数据,以及任何与服务端打交道的操作。可复用组件只实现 UI 相关的功能。

《《《《《组件职责》》》》》

约束好组件的职责,能让组件更好地解耦,知道什么功能是组件实现的,什么功能不需要实现。

组件可以分为通用组件(可复用组件)和业务组件(一次性组件)。

可复用组件实现通用的功能(不会因组件使用的位置、场景而变化):

  • UI 的展示

  • 与用户的交互(事件)

  • 动画效果

业务组件实现偏业务化的功能:

  • 获取数据

  • 和 vuex 相关的操作

  • 引用可复用组件

可复用组件应尽量减少对外部条件的依赖,所有与 vuex 相关的操作都不应在可复用组件中出现。

组件应当避免对其父组件的依赖,不要通过 this.$parent 来操作父组件的示例。父组件也不要通过 this.$children 来引用子组件的示例,而是通过子组件的接口与之交互。

《《《数据扁平化》》》》

定义组件接口时,尽量不要将整个对象作为一个 prop 传进来。

<!-- 反例 -->
<card :item="{ title: item.name, description: item.desc, poster: item.img }></card>

每个 prop 应该是一个简单类型的数据。这样做有下列几点好处:

  • 组件接口清晰

  • props 校验方便

  • 当服务端返回的对象中的 key 名称与组件接口不一样时,不需要重新构造一个对象

<card
:title="item.name"
:description="item.desc"
:poster="item.img">
</card>

扁平化的 props 能让我们更直观地理解组件的接口。

14,vue的一些问题:

1.setInterval路由跳转继续运行并没有及时进行销毁

 比如一些弹幕,走马灯文字,这类需要定时调用的,路由跳转之后,因为组件已经销毁了,但是setInterval还没有销毁,还在继续后台调用,控制台会不断报错,如果运算量大的话,无法及时清除,会导致严重的页面卡顿。

 解决方案:在组件生命周期beforeDestroy停止setInterval

beforeDestory() {
clearInterval(this.timer);
MessageBox.close()
}

2,vue.js中有两个核心功能:响应式数据绑定,组件系统

3,vue的原理(引申到数据的双向绑定)

1. 通过建立虚拟dom树`document.createDocumentFragment()`,方法创建虚拟dom树。
2. 一旦被监测的数据改变,会通过[Object.defineProperty](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty)定义的数据拦截,截取到数据的变化。
3. 截取到的数据变化,从而通过订阅——发布者模式,触发Watcher(观察者),从而改变虚拟dom的中的具体数据。
4. 最后,通过更新虚拟dom的元素值,从而改变最后渲染dom树的值,完成双向绑定

15.cookie sessionStorage localstorage 的区别?

2,vue数据的双向绑定,以及其原理(v-model)

vue,在ie9下不兼容(axios不能跨域,配置了babel-polyfill不好使,只能后端解决)

3,webpack打包项目

4,vuex的掌握情况

五大属性:

state => 基本数据 getters => 从基本数据派生的数据 mutations => 提交更改数据的方法,同步! actions => 像一个装饰器,包裹mutations,使之可以异步。 modules => 模块化Vuex

模块化:

1, dataScrSetupFlag: true, //总数据总览总设置开关(设置一些开关等,到达一些样式变化的效果)

2,项目的产品的一些配置信息

3,userName: '', // 重置密码之后的 用户名密码 跳转登陆的时候填入

4,api接口路径:百度一下,你就知道

16,项目中的难点,亮点

在vue项目中关于定时器的使用(难点)

假如,我们在A组建中声明了一个定时器进行倒计时此时 我们在定时器中一直打印时间戳,此时我们通过路由跳转到下一个组件页面,按照正常理论来说,A组件在跳转后整个组件就会被销毁,到达B组件时A组件已经没有了。

但是在我们有定时器的情况下 此时跳转过后 定时器依然会存在。所以我们要在页面销毁时候将当前页面的定时器clear掉。(比如,统计页面,定时刷新数据)

2:比如数组去重,使用了效率更高的写法()

3,有些后端为了性能优化,多数情况,避免前端频繁的请求后端接口,一次返回数据,让前端去调用排序,搜索等

17,react可能会问到(vue也会用到diff算法)

Diff算法 的运行机制?

概念: diff算法是一种优化手段,将前后两个模块进行差异对比,修补(更新)差异的过程叫做patch(打补丁)

通俗的讲,当前层级的dom 先进行对比,如果发现异同 就更新当前dom 和 它的子节点

<!--为什么vue,react这些框架中都会有diff算法呢? 我们都知道渲染真实dom的开销是很大的,这个跟性能优化中的重绘重排意义类似, 回到正题来, 有时候我们修改了页面中的某个数据,如果直接渲染到真实DOM中会引起整棵数的重绘重排, 那么我们能不能只让我们修改的数据映射到真实DOM, 做一个最少化重绘重排呢-->

注:Key值唯一 提高虚拟dom的对比效率

问:Key值的作用 为什么最好不要用index???

Index 索引可以随着队列的操作发生变化 不利于虚拟dom之间的对比

[

[id:004,小李] 0

[id:001,小明] 1

[id:002,小红] 2

[id:003,小花] 3

]

总结

  • 尽量不要跨层级的修改dom

  • 在开发组件时,保持稳定的 DOM 结构会有助于性能的提升

  • 设置key可以让diff更高效

18,组件数据传递时候延迟怎么办

<echarts-com v-if="datamenu.length>0" ids="nike" widths='700px' />

加上判断就行,这个时候,数据如果没有就不显示当前组件,有数据在显示

19,面试问题1

Get和post的区别 ,Tcp三次握手,函数节流防抖,ajax和axios区别

最后

以上就是稳重睫毛膏为你收集整理的前端问题总结16,项目中的难点,亮点17,react可能会问到(vue也会用到diff算法)的全部内容,希望文章能够帮你解决前端问题总结16,项目中的难点,亮点17,react可能会问到(vue也会用到diff算法)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(48)

评论列表共有 0 条评论

立即
投稿
返回
顶部