1、js
面向对象、面向过程。
面向对象:
面向对象是一种程序开发的方法,它将对象作为程
序的基本单元,将程序和数据封装其中,以提高软
件的灵活性、重用性和扩展性。对象是把数据及对
数据的操作方法放在一起,作为一个相互依存的整
体。再说一下类与对象,类描述了一组有相同特性
和相同行为的对象,具有相同属性和相同方法的对象的抽象就是类。即,对象的抽象是类,类的实例是对象。在面向对象的编程中,把用类创建对象的过程称为实例化。
面向过程与面向对象的区别
面向过程是一种直接的编程方法,它是按照编程语言的思路考虑问题。通过顺序执行一组语句来实现一个功能,这些语句的执行过程就是整个程序。
面向对象是一种抽象度更高的编程方法。它的目标是使模块的抽象度更高,目的是可复用。
面向对象三大特性:封装,继承,多态。
面向过程可以说是从细节方面思考问题
面向对象可以说是从宏观方面思考问题
对象的创建方式有对象字面量,工厂方式,原型方式,构造函数,混合模式。==
对象的创建-字面量,原型,构造函数等各自特点
(你开发过什么什么插件)
1.字面量
<script>
var obj={
name:"Join",
sayHello:function(){
console.log(this.name)
}
}
var obj1=new Object()
obj1.name="Mary"
console.log(obj,obj1)
</script>
2.工厂模式
<script>
function creatObject(name){
var obj=new Object();
obj.name=name;
obj.sayHello=function(){
console.log(this.name)
}
return obj;
}
var obj1=creatObject("John")
var obj2=creatObject("Mary")
obj1.sayHello()
obj2.sayHello()
</script>
3.构造函数
<script>
//可以把构造函数看成是某个类型
//一个构造函数加new运算符之后 会进行一下4步操作
/*
* 1.创建一个新对象
* 2.改变构造函数里的this指向,指向这个新创建的对象
* 3.执行构造函数里的代码
* 返回这个新对象
*/
function Person(name){
this.name=name;//
this.sayHello=function(){
console.log(this.name)//Apple
}
}
var person= new Person("Apple")
var person1=new Person("Apple1")
person.sayHello()
console.log(person instanceof Person)//true
console.log(person instanceof Object)//true
//每创建一个实例,就会开辟一块内存空间,保存实例方法
console.log(person.sayHello==person1.sayHello)//false
</script>
4.借助原型对象
<script>
//原型对象上的属性和方法能被实例所访问
function Person(){
}
Person.prototype.name="John"
Person.prototype.sayHello=function(){
console.log(this.name)
}
var person=new Person()
person.sayHello()
var person1=new Person()
console.log(person.sayHello==person1.sayHello)//true
</script>
5.组合方式
<script>
//属性放到构造函数里,方法放到原型对象上
function Person(name,age){
this.name=name;
this.age=age;
}
Person.prototype.sayHello=function(){
console.log(this.name)
}
var person=new Person("Mary")
person.sayHello()
var person1=new Person("Mary1")
console.log(person.sayHello==person1.sayHello)//true
</script>
继承 - 原型,原型连,es6继承 ,call,apply
原型继承:
每一个构造函数都有一个原型对象,每一个原型对象都有一个指针constructor指向构造函数
每一个实例都有一个内部指针(proto),指向原型对象,原型对象上的属性和方法能够被实例访问
函数都有prototype,每一个实例对象都有__proto__
混合继承:
使用原型链与构造函数继承
构造函数继承 只能继承 实例属性
原型链继承 只能继承方法
ES6中的继承
ES6 要求,子类的构造函数必须执行一次super函数
子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象。
<script>
class Person{
constructor(name,age) {
this.name=name;
this.age=age;
}
Hello(){
console.log(this.name)
}
}
class Male extends Person{
constructor(name,age){
super(name,age)
}
}
var male=new Male('Join',20)
male.Hello()
</script>
call和apply
call方法的第一个参数的值赋值给类(即方法)中出现的this
//call方法的第二个参数开始依次赋值给类(即方法)所接受的参数
//以列表形式传参
apply方法接受2个参数,
// A、第一个参数与call方法的第一个参数一样,即赋值给类(即方法)中出现的this
// B、第二个参数为数组类型,这个数组中的每个元素依次赋值给类(即方法)所接受的参数
// 以数组形式传参
<script>
function Person(name,age){
this.name=name;
this.age=age;
this.hello=function(){
console.log(this.name+this.age)
}
}
function Male(name,age){
//继承父类Person call方法继承
//call方法的第一个参数的值赋值给类(即方法)中出现的this
//call方法的第二个参数开始依次赋值给类(即方法)所接受的参数
//以列表形式传参
Person.call(this,name,age)
}
function Female(name,age){
// apply方法接受2个参数,
// A、第一个参数与call方法的第一个参数一样,即赋值给类(即方法)中出现的this
// B、第二个参数为数组类型,这个数组中的每个元素依次赋值给类(即方法)所接受的参数
// 以数组形式传参
Person.apply(this,[name,age])
}
var yyz=new Male('yyz',20)
yyz.hello()
var ywy=new Female('aaa',18)
ywy.hello()
</script>
利用apply使用math的min和max 方法
var arr1 = [1,2,3,4,5,6,6,45,45];
console.log(Math.min.apply(null,arr1));
console.log(Math.max.apply(null,arr1));
闭包-是什么,什么情况会出现,项目中用它处理什么
在一个函数外部,可以访问改函数内部局部变量的函数
function foo(){
var a=1 //在函数内部定义的a是局部变量
return function(){
console.log(a++)
}
}
var bar=foo();
console.log(bar)//ƒ (){console.log(a++) }
bar() //1
bar()//2
bar()//3
foo()()//1
foo()()//1
foo()()//1
闭包的应用:
<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
<script>
let aLis = document.querySelectorAll('li')
var obj=(function(){
var a=10;
var b=20;
function foo(){
console.log(a)
}
function bar(){
console.log(b)
}
return {
foo:foo,
bar:bar
}
})()
obj.foo()
console.log(obj.b)//undefined
</script>
</body>
闭包的特点
1:可以读取函数内部的变量。
变量的作用域无非就是两种:全局变量和局部变量。
JS语言的特殊之处,就在于函数内部可以直接读取全局变量。另一方面,函数外部自然无法读取函数内的局部变量
2:让这些变量的值始终保存在内存中。
闭包的好处:
减少全局变量和全局函数的定义,减少内存的占用
闭包的应用场景
1:函数作为返回值。
2:函数作为参数被传递
回调函数
<body>
<script>
//减少全局函数或全局变量的定义,减少内存占用
var obj=(function(){
var a=10;
var b=20;
function foo(){
console.log(a)
}
function bar(){
console.log(b)
}
return {
foo:foo,
bar:bar
}
})()
obj.foo()
</script>
</body>
闭包面试题:
<body>
<script>
function fun(n,o){
console.log(o)
return {
fun:function(m){
return fun(m,n)
}
}
}
var a=fun(0)//undefined n:0
a.fun(1)//fun(1,0) 0
a.fun(2)//fun(2,0) 0
a.fun(3)//fun(3,0) 0
var b=fun(0).fun(1).fun(2).fun(3) //undefined 0 1 2
var c=fun(0).fun(1)//0
c.fun(2)//1
c.fun(3)//1
</script>
</body>
es6相对于es5新增技术,你常用哪些?
es5:
1.strict模式
严格模式,限制一些用法,‘use strict’;
- Array增加方法
增加了every、some 、forEach、filter 、indexOf、lastIndexOf、isArray、map、reduce、reduceRight方法
PS: 还有其他方法 Function.prototype.bind、String.prototype.trim、Date.now
- Object方法
Object.getPrototypeOf
Object.create
Object.getOwnPropertyNames
Object.defineProperty
Object.getOwnPropertyDescriptor
Object.defineProperties
Object.keys
Object.preventExtensions /
Object.isExtensible
Object.seal / Object.isSealed
Object.freeze / Object.isFrozen
Es6
ECMAScript6在保证向下兼容的前提下,提供大量新特性
1.块级作用域 关键字let, 常量const
2.对象字面量的属性赋值简写(property value shorthand)
3.赋值解构
4.函数参数 - 默认值、参数打包、 数组展开(Default 、Rest 、Spread)
5.箭头函数 Arrow functions
6.字符串模板 Template strings
- Iterators(迭代器)+ for…of
8.生成器 (Generators)
9.Class
Class,有constructor、extends、super,但本质上是语法糖(对语言的功能并没有影响,但是更方便程序员使用)。
10.Modules
ES6的内置模块功能借鉴了CommonJS和AMD各自的优点:
(1).具有CommonJS的精简语法、唯一导出出口(single exports)和循环依赖(cyclic dependencies)的特点。
(2).类似AMD,支持异步加载和可配置的模块加载。
11.Map + Set + WeakMap + WeakSet
四种集合类型,WeakMap、WeakSet作为属性键的对象如果没有别的变量在引用它们,则会被回收释放掉。
12.Math + Number + String + Array + Object APIs
- Proxies
使用代理(Proxy)监听对象的操作,然后可以做一些相应事情。
14.Symbols
Symbol是一种基本类型。Symbol
通过调用symbol函数产生,它接收一个可选的名字参数,该函数返回的symbol是唯一的。
15.Promises
Promises是处理异步操作的对象,使用了 Promise 对象之后可以用一种链式调用的方式来组织代码,让代码更加直观(类似jQuery的deferred 对象)。
promise怎么用的?用它做过什么功能?
异步编程的解决方案,解决回调函数的嵌套问题
登录的时候,有些内容需要用户登录后才进行显示
resolve函数的作用是 在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
reject函数的作用是 在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去
Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。
then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。其中,第二个函数是可选的,不一定要提供。这两个函数都接受Promise对象传出的值作为参数。
<script>
let p = new Promise(function(resolve,reject){
let url = 'http://jx.xuzhixiang.top/ap/api/allproductlist.php?pagesize=20&pagenum=4'
ajax(url,function(res){
console.log(res)
resolve(res)
},function(erorr){
reject(error)
})
})
p.then(function(r){
console.log(r)
return new Promise(function(resolve,reject){
let url = 'http://jx.xuzhixiang.top/ap/api/allproductlist.php?pagesize=20&pagenum=4'
ajax(url,function(res){
console.log(res)
resolve(res)
},function(erorr){
reject(error)
})
})
}) .then(function(res){
console.log(res)
let url = 'http://jx.xuzhixiang.top/ap/api/allproductlist.php?pagesize=20&pagenum=4'
ajax(url,function(res){
console.log(res)
resolve(res)
},function(erorr){
reject(error)
})
}).catch(function(error){
console.log(error)
})
</script>
ajax交互的底层原理,如何封装ajax;
Ajax的原理简单来说通过XmlHttpRequest对象来向服务器发异步请求,从服务器获得数据,然后用javascript来操作DOM而更新页面。这其中最关键的一步就是从服务器获得请求数据。要清楚这个过程和原理,我们必须对 XMLHttpRequest有所了解。
XMLHttpRequest是ajax的核心机制,它是在IE5中首先引入的,是一种支持异步请求的技术。简单的说,也就是javascript可以及时向服务器提出请求和处理响应,而不阻塞用户。达到无刷新的效果。
所以我们先从XMLHttpRequest讲起,来看看它的工作原理。
首先,我们先来看看XMLHttpRequest这个对象的属性。
它的属性有:
onreadystatechange 每次状态改变所触发事件的事件处理程序。
responseText 从服务器进程返回数据的字符串形式。
responseXML 从服务器进程返回的DOM兼容的文档数据对象。
status 从服务器返回的数字代码,比如常见的404(未找到)和200(已就绪)
status Text 伴随状态码的字符串信息
readyState 对象状态值
0 (未初始化) 对象已建立,但是尚未初始化(尚未调用open方法)
1 (初始化) 对象已建立,尚未调用send方法
2 (发送数据) send方法已调用,但是当前的状态及http头未知
3 (数据传送中) 已接收部分数据,因为响应及http头不全,这时通过responseBody和responseText获取部分数据会出现错误,
4 (完成) 数据接收完毕,此时可以通过通过responseXml和responseText获取完整的回应数据
ajax,axios,fetch区别对比
1、ajax的优势
1:无刷新更新数据。(局部刷新)
2:异步与服务器通信。(不让界面卡顿)
3:基于标准被广泛支持。
4:界面与应用分离。(前后端分离)
5:节省带宽
ajax缺点:
- 本身是针对MVC的编程,不符合现在前端MVVM的浪潮
- 基于原生的XHR开发,XHR本身的架构不清晰,已经有了fetch的替代方案
- JQuery整个项目太大,单纯使用ajax却要引入整个JQuery非常的不合理
ajax的封装:
<script>
function ajax(url, callbackfn,errorcallback) {
let xhr = new XMLHttpRequest();
xhr.open('GET', url)
xhr.send(null)
xhr.onload = function() {
if (xhr.status == 200) {
//let str=xhr.responseText
let jsonStr = xhr.responseText
let obj = JSON.parse(jsonStr)
console.log(obj)
callbackfn(obj)
return obj
} else {
if (errorcallback) {
errorcallback(xhr.responseText)
}
}
}
}
</script>
2、axios
axios({
method: 'post',
url: '/user/12345',
data: {
firstName: 'Fred',
lastName: 'Flintstone'
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
axios 是一个基于Promise 用于浏览器和 nodejs 的 HTTP 客户端,本质上也是对原生XHR的封装,只不过它是Promise的实现版本,符合最新的ES规范,它本身具有以下特征:
1.从浏览器中创建 XMLHttpRequest
2.支持 Promise API
3.客户端支持防止CSRF
4.提供了一些并发请求的接口(重要,方便了很多的操作)
5.从 node.js 创建 http 请求
6.拦截请求和响应
7.转换请求和响应数据
8.取消请求
9.自动转换JSON数据
3、fetch
fetch号称是AJAX的替代品,是在ES6出现的,使用了ES6中的promise对象。Fetch是基于promise设计的。Fetch的代码结构比起ajax简单多了,参数有点像jQuery ajax。但是,一定记住fetch不是ajax的进一步封装,而是原生js,没有使用XMLHttpRequest对象。
try {
let response = await fetch(url);
let data = response.json();
console.log(data);
} catch(e) {
console.log("Oops, error", e);
}
fetch的优点:
1. 语法简洁,更加语义化
2. 基于标准 Promise 实现,支持 async/await
3. 同构方便,使用 [isomorphic-fetch](https://github.com/matthew-andrews/isomorphic-fetch)
4.更加底层,提供的API丰富(request, response)
5.脱离了XHR,是ES规范里新的实现方式
==总结:
axios既提供了并发的封装,也没有fetch的各种问题,而且体积也较小,当之无愧现在最应该选用的请求的方式。==
深拷贝,浅拷贝
引用类型直接复制 浅拷贝
引用类型的内部的值的复制,与地址无关 深拷贝
var a=10;
var b=a
// 更换值,a,b互不影响
引用类型拷贝:
var a=[10];
var b=a;
b=[20]//这里是给b一个新的地址
console.log(a)//打印结果为10
var a=[10];
var b=a;
b.push(20)//这里在b原有的地址上加了20
console.log(a)//打印结果为[10],[20]
引用类型直接复制 浅拷贝
引用类型直接复制(或赋值)称为浅拷贝。 引用类型内部的值的赋值,与地址无关,称为深拷贝
【补充: 假设B复制了A,修改A的时候,看B是否发生变化:
如果B跟着也变了,说明是浅拷贝,拿人手短!(修改堆内存中的同一个值)
如果B没有改变,说明是深拷贝,自食其力!(修改堆内存中的不同的值)
浅拷贝(shallowCopy)只是增加了一个指针指向已存在的内存地址,
深拷贝(deepCopy)是增加了一个指针并且申请了一个新的内存,使这个增加的指针指向这个新的内存,
使用深拷贝的情况下,释放内存的时候不会因为出现浅拷贝时释放同一个内存的错误。
浅复制:仅仅是指向被复制的内存地址,如果原地址发生改变,那么浅复制出来的对象也会相应的改变。
深复制:在计算机中开辟一块新的内存地址用于存放复制的对象。】
Object.assign()//将一个或多个原对象的属性复制给目标对象。
只能实现一维数组的深拷贝
用法:
var obj1={a:1,b:2};
var obj={};
Object.assign(obj,obj1);
console.log(obj)//打印值为{a:1,b:2}
obj.a=10;
console.log(obj1)//打印值为{a:1,b:2}
var obj1={a:[1],b:2};
var obj={};
Object.assign(obj,obj1);
console.log(obj)//打印值为{a:1,b:2}
obj.a.push(2);
console.log(obj,obj1)//打印值为{a:[1,2],b:2}{a:[1,2],b:2}
var obj1={a:[1],b:2};
var obj2={c:3,d:4};
var obj={}
object.assign(obj,obj1,obj2)
console.log(obj)//打印值为:{a:[1],b:2,c:3,d:4}
var obj1={a:[1],b:2};
var obj2={a:3,d:4};
var obj={}
object.assign(obj,obj1,obj2)
console.log(obj)//打印值为:{a:3,b:2,d:4}证明如果有重复的值名,后面会覆盖前面的。
Objext.defineProperty()定义一个对象的属性,这些属性具有可配置性。
var obj={}
Objext.defineProperty(obj,'a',{
value:1
})
console.log(obj)//打印结果{a:1}
var obj={}
Objext.defineProperty(obj,'a',{
value:1
enumerable:true//默认不可遍历,加上 enumerable:true允许遍历
})
console.log(obj)//打印结果{a:1}
for(var i in obj){
console.log(i)//打印结果为a
}
//Writable:true可修改,false不可修改
//Configurable:false 不可删除,true可删除
深拷贝方法封装:
function deepCopy(obj){
//Array.isArray(obj)判断小括号里的参数是不是一个数组
if(Array.isArray(obj)){
var newObj=[]
}else{
var newObj={}
}
for(var i in obj){
if(typeof obj[i]=="object"){
newObj[i]=deepCopy(obj[i])
}else{
newObj[i]=obj[i]
}
}
return newObj;
}
var arr=[[1],2,3]
var arr1=deepCopy(arr)
arr1[0].push(2)
console.log(arr,arr1)
console.log(deepCopy([[1],2,3]))
深拷贝的第二种方法:
可以实现多维数组的深拷贝
var obj={a:[1,2,3]}
var obj1=JSON.parse(JSON.stringify(obj))
obj1.a.push(2)
console.log(obj,obj1)
常用数组方法,数组-字符串转换