1.原型链
每个对象都会在其内部初始化一个属性,就是prototype(原型),
当我们访问一个对象的属性时,
如果这个对象内部不存在这个属性,那么他就会去prototype里找这个属性,这个prototype又会有自己的prototype, 于是就这样一直找下去,也就是我们平时所说的原型链的概念。

关系:instance.constructor.prototype = instance.-proto-

特点: JavaScript对象是通过引用来传递的,我们创建的每个新对象实体中并没有一份属于自己的原型副本。当我们修改原型时,与之相关的对象也会继承这一改变。

2.闭包
闭包是指有权访问另一个函数作用域中变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量,利用闭包可以突破作用链域,将函数内部的变量和方法传递到外部。
闭包的优点
- 创建一个匿名函数,并立即执行,加强了安全性和封装性,由于外部无法引用内部变量,不会造成全局污染。
- 缓存,可以将值存储起来,当需要调用的时候,首先在缓存中查找,如果找不 到再进行计算,然后更新缓存并返回值
- 实现封装和实现面向对象中的对象。
闭包的缺点
- 闭包会导致变量不能释放,引起内存泄露。
示例
function say667() {
// Local variable that ends up within closure
var num = 666;
var sayAlert = function() {
alert(num);
}
num++;
return sayAlert;
}
var sayAlert = say667();
sayAlert()//执行结果应该弹出的667
执行say667()后,say667()闭包内部变量会存在,而闭包内部函数的内部变量不会存在
使得Javascript的垃圾回收机制GC不会收回say667()所占用的资源
因为say667()的内部函数的执行需要依赖say667()中的变量
这是对闭包作用的非常直白的描述
3.作用域
变量的作用域无非就是两种:全局变量和局部变量。
全局作用域:
最外层函数定义的变量拥有全局作用域,即对任何内部函数来说,都是可以访问的:
var outerVar = "outer";
function fn(){
console.log(outerVar);
}
fn();//result:outer
局部作用域:
和全局作用域相反,局部作用域一般只在固定的代码片段内可访问到,而对于函数外部是无法访问的,最常见的例如函数内部
<script>
function fn(){
var innerVar = "inner";
}
fn();
console.log(innerVar);// ReferenceError: innerVar is not defined
</script>
作用域的特点
次序性和向上线程性搜索
4、Ajax请求(jQuery方式)
定义
无需重新加载整个网页的情况下,能够更新部分网页的技术
ajax的全称:Asynchronous Javascript And XML。(异步传输+js+xml)
请求方式
$.ajax()返回其创建的 XMLHttpRequest 对象。
$.ajax({
type: "get",
dataType: "application/json",
url: '/Ajax.json',
success: function (data) {
}
});
==在ajax请求中type的值分为(GET、POST、DELETE)最常见的==
GET:获取服务端数据,用get方式可传送简单数据,但大小一般限制在1KB下,数据追加到url中发送(http的header传送)。安全性能高。
POST:上传数据到服务端,数据上传无大小限制,但是需要设置content—type请求头;
DELETE:用于删除数据。
加载方式
-
同步:单线程运行,发送方发出数据后,等接收方发回响应以后才发下一个数据包的通讯方式。用户填写所有信息后,提交给服务器,等待服务器的回应(检验数据),是一次性的。信息错误又要重新填写!
客户端请求(等待)->服务端处理->响应->页面载入 -
异步:发送方发出数据后,不等接收方发回响应,接着发送下个数据包的通讯方式。
页面上的操作和服务器端的操作互相之间不会造成阻塞
(1)new一个XHR对象
(2)调用open方法
(3)send一些数据
(4)对过程进行监听,来知道服务器是不是正确地做出了响应,接着可以做一些事情
跨域解决方式
- jsonp、
- iframe、
- window.name、
- window.postMessage、
- 服务器上设置代理页面(CORS)
5. 数据拷贝的方法
1、数据相互赋值实现拷贝
let a=[0,1,2,3,4],
b=a;
console.log(a===b);
b[0]=1;
console.log(a,b);
在这里打印a和b的时候,我们明明b复制了a,但修改数组a,数组b也跟着变了。而当我们a[0]=1时进行数组修改时,由于a与b指向的是同一个地址,所以自然b也受了影响,这就是所谓的浅拷贝了。
浅拷贝出现地址引用的原因:
而对于对象这种内存占用比较大的来说,直接让复制的东西等于要复制的,那么就会发生引用,因为这种复制,只是将复制出来的东西的指向指向了要复制的那个东西,简单的说,就是两个都同时指向了一个空间,如果改变其中一个,另一个也会发生变化。这就发生了引用。
** 引用数据类型--名存在栈内存中,值存在于堆内存中,但是栈内存会提供一个引用的地址指向堆内存中的值**

2.对象使用递归实现拷贝
function deepClone(obj){
let objClone = Array.isArray(obj)?[]:{};
if(obj && typeof obj==="object"){
for(key in obj){
if(obj.hasOwnProperty(key)){
//判断ojb子元素是否为对象,如果是,递归复制
if(obj[key]&&typeof obj[key] ==="object"){
objClone[key] = deepClone(obj[key]);
}else{
//如果不是,简单复制
objClone[key] = obj[key];
}
}
}
}
return objClone;
}
let a=[1,2,3,4],
b=deepClone(a);
a[0]=2;
console.log(a,b);
递归递归去复制所有层级属性的拷贝方式,是开辟一个新的内存专门为b存放值,这样就实现了深拷贝。
在ES6中的,对于数组的拷贝复制又有新的方式
- Array.from(要复制的数组):
var arr1=[1,2,3];
var arr2=Array.from(arr1);
arr1.push(4);
alert(arr1); //1234
alert(arr2); //123
arr2.push(5);
alert(arr1); //1234
alert(arr2); //1235
- 对象转字符串再将其转回对象的方式
let a =[{a:1}]
let b = JSON.parse(JSON.stringify(a))
b[0].a=3
consloe.log(a[0].a) // 1
用json转才可以完成深赋值,因为字符串转成对象之后就是一个新的对象了;
- ...方式
var arr1=[1,2,3];
var arr2=[...arr1];
arr1.push(4);
alert(arr1); //1234
alert(arr2); //123
arr2.push(5);
alert(arr1); //1234
alert(arr2); //1235
6. 数组去重方式
1. 循环对比
双层循环,外层循环元素,内层循环时比较值,相同的值则跳过,不相同则push进数组
Array.prototype.distinct = function(){
var arr = this,
result = [],
i,
j,
len = arr.length;
for(i = 0; i < len; i++){
for(j = i + 1; j < len; j++){
if(arr[i] === arr[j]){
j = ++i;
}
}
result.push(arr[i]);
}
return result;
}
var arra = [1,2,3,4,4,1,1,2,1,1,1];
arra.distinct(); //返回[3,4,2,1]
2. 属性对比去重
利用对象的属性不能相同的特点进行去重
Array.prototype.distinct = function (){
var arr = this,
i,
obj = {},
result = [],
len = arr.length;
for(i = 0; i< arr.length; i++){
if(!obj[arr[i]]){ //如果能查找到,证明数组元素重复了
obj[arr[i]] = 1;
result.push(arr[i]);
}
}
return result;
};
var a = [1,2,3,4,5,6,5,3,2,4,56,4,1,2,1,1,1,1,1,1,];
var b = a.distinct();
console.log(b.toString()); //1,2,3,4,5,6,56
3. 利用ES6的set
Set数据结构,它类似于数组,其成员的值都是唯一的。
function dedupe(array){
return Array.from(new Set(array));
}
dedupe([1,1,2,3]) //[1,2,3]
7.call和apply的使用
call和apply,它们的作用都是将函数绑定到另外一个对象上去运行
/*apply()方法*/
function.apply(thisObj[, argArray]);
/*call()方法*/
function.call(thisObj[, arg1[, arg2[, [,...argN]]]]);
它们各自的定义:
apply:调用一个对象的一个方法,用另一个对象替换当前对象。例如:B.apply(A, arguments);即A对象应用B对象的方法。
call:调用一个对象的一个方法,用另一个对象替换当前对象。例如:B.call(A, args1,args2);即A对象调用B对象的方法。
它们的共同之处:
都“可以用来代替另一个对象调用一个方法,将一个函数的对象上下文从初始的上下文改变为由thisObj指定的新对象”。
它们的不同之处:
apply:最多只能有两个参数——新this对象和一个数组argArray。如果给该方法传递多个参数,则把参数都写进这个数组里面,当然,即使只有一个参数,也要写进数组里。如果argArray不是一个有效的数组或arguments对象,那么将导致一个TypeError。如果没有提供argArray和thisObj任何一个参数,那么Global对象将被用作thisObj,并且无法被传递任何参数。
call:它可以接受多个参数,第一个参数与apply一样,后面则是一串参数列表。这个方法主要用在js对象各方法相互调用的时候,使当前this实例指针保持一致,或者在特殊情况下需要改变this指针。如果没有提供thisObj参数,那么 Global 对象被用作thisObj。
网友评论