1.原型 構(gòu)造函數(shù) 實例
- 原型(prototype):每當(dāng)定義一個對象(函數(shù)也是對象)的時候,對象都會包含一些預(yù)定義的屬性验游,其中每個函數(shù)對象都包含一個prototype屬性,這個屬性指向函數(shù)的原型對象
- 構(gòu)造函數(shù):可以通過new 來新建一個對象的函數(shù)
- 實例:通過構(gòu)造函數(shù)和new創(chuàng)建出來的對象就是實例,實例通過
_proto_
指向原型碧信,通過constructor指向構(gòu)造函數(shù) - 每個對象都有
_proto_
屬性,但是只有函數(shù)對象才有prototype屬性
例子:
var obj={}等同于 var obj=new Object();
obj是構(gòu)造函數(shù)(Object)的一個實例
obj.prototype就是一個原型對象(obj.prototype==原型對象)
obj._proto_ == Object.prototype
obj.constructor == Object
Object.prototype.constructor == Object
var person1 = new Person()
1.person1._proto_
是什么?
因為person1._proto_ === person1的構(gòu)造函數(shù).prototype
因為person1的構(gòu)造函數(shù) === Person
所以 person1._proto_ === Person.prototype
2.Person._proto_
是什么
因為Person._proto_ === Person的構(gòu)造函數(shù).prototype
因為Person的構(gòu)造函數(shù) === Function
所以Person._proto_ ===Function.prototype
3.Person.prototype._proto_
是什么?
因為Person.prototype是一個普通的對象街夭,一個普通對象的構(gòu)造函數(shù) ===Object
所以 Person.prototype._proto_ ===Object.prototype
4.Object.prototype._proto_
是什么
Object.prototype對象也有_proto_
屬性砰碴,但是比較特殊,為null,處于原型鏈的頂端板丽,所以Object.prototype._proto_ ===null
2.原型鏈
原型鏈是由原型對象組成呈枉,每個對象都有_proto_
屬性,指向了創(chuàng)建該對象的構(gòu)造函數(shù)和原型埃碱,_proto_
將原型對象鏈接起來組成了原型鏈猖辫,是用來實現(xiàn)繼承和共享屬性的有限對象鏈。
- 屬性查找機制:當(dāng)查找對象屬性時砚殿,如果實例對象自身不存在該屬性啃憎,則沿著原型鏈向上一級查找,找到則輸出似炎,如果沒找到辛萍,則沿著原型鏈繼續(xù)向上查找,直到最頂層原型對象Object.prototype名党,如果還沒有則輸出undefined
- 屬性修改機制:只會修改實例對象本身的屬性叹阔,如果不存在,則進行添加該屬性传睹,如果需要修改原型的屬性時耳幢,則可以用: b.prototype.x = 2;但是這樣會造成所有繼承于該對象的實例的屬性發(fā)生改變欧啤。
3.函數(shù)柯里化
在一個函數(shù)中睛藻,首先添加幾個固定的參數(shù),在返回一個新的函數(shù)邢隧,稱為函數(shù)的柯里化店印,可以多次重復(fù)調(diào)用。
ES5實現(xiàn)高階函數(shù)也叫函數(shù)的柯里化
function add(x){
return function(y){
return x+y
}
}
var add2= add(2);
add2(3); // => 5
add(10)(11); // => 21
ES6箭頭函數(shù)實現(xiàn)函數(shù)柯里化
const add= x=> y=> x+y;
4.閉包
function init() {
var name = "Mozilla"; // name 是一個被 init 創(chuàng)建的局部變量
function displayName() { // displayName() 是內(nèi)部函數(shù),一個閉包
alert(name); // 使用了父函數(shù)中聲明的變量
}
displayName();
}
init();
5.對象的深淺拷貝
對象的拷貝在js中比較常見
- 淺拷貝:僅僅復(fù)制對象的引用倒慧,而不是對象本身按摘,一旦修改拷貝對象包券,原對象也會被修改
- 深拷貝:把復(fù)制的對象所有引用的對象全部拷貝包括地址,修改拷貝的對象炫贤,原對象不會改變溅固。
1.淺拷貝
淺拷貝的方法比較簡單,只是簡單復(fù)制引用兰珍,,如果我們要復(fù)制對象的屬性不引用類型就可以使用淺拷貝侍郭,
1.簡單的賦值也屬于淺拷貝
var obj = { age: '12',name:'jack' };
var newObj = obj;
//此時將obj中的age屬性值改變,會發(fā)現(xiàn)newObj中的age屬性值跟著改變
obj.age = '13'; //obj: { age: '13' } newObj: { age: '13',name:''jack}
2.遍歷復(fù)制掠河,返回新對象
function shallowCopy(obj) {
var copy = {};
// 只復(fù)制可遍歷的屬性
for (key in obj) {
// 只復(fù)制本身擁有的屬性
if (obj.hasOwnProperty(key)) {
copy[key] = obj[key];
}
}
return copy;
}
obj.hasOwnProperty() MDN解釋
這個方法可以用來檢測一個對象是否含有特定的自身屬性
語法:obj.hasOwnProperty(prop)
參數(shù):要檢測的屬性 字符串 名稱或者 Symbol
返回值: 用來判斷某個對象是否含有指定的屬性的 Boolean
3.js內(nèi)部的淺拷貝
var newObj = Object.assign({}, originObj);其中第一個參數(shù)是我們最終復(fù)制的目標(biāo)對象亮元,后面的所有參數(shù)是我們的即將復(fù)制的源對象,支持對象或數(shù)組唠摹。
4.數(shù)組的淺拷貝
[].slice();
5.展開運算符
{...a}
2深拷貝
常用的有JSON.parse(),遞歸深拷貝爆捞,還有es5的object.create();
1.JSON.parse()
這種方法不支持json /正則/Data/undefined等
var a = {...}
var b = JSON.parse( JSON.stringify(a) )
2.遞歸深拷貝
function copy(obj1, obj2) {
var obj = obj2 || {};
for (var i in obj1) {
var prop = obj1[i];
// 避免相互引用對象導(dǎo)致死循環(huán),如obj1.a = obj1的情況
if(prop === obj) {
continue;
}
if (typeof prop === 'object') {
obj[i] = (prop.constructor === Array) ? [] : {};
arguments.callee(prop, obj[i]);
} else {
obj[i] = prop;
}
}
return obj;
}
3.object.create()
function deepClone(obj1, obj2) {
var obj = obj2|| {};
for (var i in obj1) {
var prop = obj1[i];
// 避免相互引用對象導(dǎo)致死循環(huán)勾拉,如obj1.a = obj1的情況
if(prop === obj) {
continue;
}
if (typeof prop === 'object') {
obj[i] = (prop.constructor === Array) ? prop : Object.create(prop);
} else {
obj[i] = prop;
}
}
return obj;
}
6.代碼的復(fù)用
當(dāng)你發(fā)現(xiàn)代碼多次重復(fù)的時候嵌削,應(yīng)該考慮代碼的復(fù)用。
- 函數(shù)封裝
- 繼承
- 復(fù)制extend
7.模塊化
模塊化開發(fā)是現(xiàn)在前端開發(fā)的重要部分望艺,模塊化提高了項目的可維護性和可拓展性、可協(xié)作性肌访,在瀏覽器中ES6支持模塊化找默,在Node中使用commonjs的模塊支持
- es6 :import /export
- commonjs:require /module.exports /exports
- amd: require /defined
1.require支持動態(tài)導(dǎo)入,import不支持
2.require是同步導(dǎo)入吼驶,import是異步導(dǎo)入
3.require是值拷貝惩激,導(dǎo)出值不會影響導(dǎo)入值,import指向內(nèi)存地址蟹演,導(dǎo)入值會隨導(dǎo)出值變化
8.this
1.全局環(huán)境/普通函數(shù)
在全局函數(shù)中风钻,this永遠指向window,普通函數(shù)的調(diào)用,不是構(gòu)造函數(shù)沒有使用new,其中this也是指向window
var a = 1;
function fen(){
console.log(this);//window
console.log(this.a)//1
}
fen();
2.構(gòu)造函數(shù)的this指向
構(gòu)造函數(shù)就是指函數(shù)new出來的一個對象酒请,
function Fen(){
this.a= 1;
console.log(this)//Fen{a:1}
}
var foo= new Fen();
console.log(foo.a)//10
如果函數(shù)作為構(gòu)造函數(shù)使用骡技,其中this就代表他new出來的對象,如果直接調(diào)用Fen()函數(shù)羞反,而不是new Fen()布朦,那么Fen()就是普通函數(shù),this還是指向全局window
3.對象方法
如果函數(shù)作為對象的方法時昼窗,那么this指向該對象
var obj = {
a:1,
foo:function(){
console.log(this)//Object
console.log(this.a)//1
}
}
obj.foo();
如果在對象方法中定義函數(shù)是趴,
var obj = {
a:1,
foo:function(){
function a(){
console.log(this)//window
console.log(this.a)//undefined
}
a();
}
}
obj.foo();
函數(shù)a雖然在obj.foo內(nèi)部定義,但是依然是一個普通函數(shù),this指向window,如果想調(diào)用變量a.可以把this.保存起來
var obj = {
a:1,
foo:function(){
var that = this
function a(){
console.log(that)//{a:1}
console.log(that.a)//1
}
a();
}
}
obj.foo();
如果foo函數(shù)不作為對象方法被調(diào)用
var obj = {
a:1,
foo:function(){
console.log(this)//window
console.log(this.a)//undefined
}
}
var fn=obj.foo();
fn();
obj.foo被賦值給一個全局變量,并沒有作為obj的一個屬性被調(diào)用澄惊,那么此時this還是指向全局window
4.構(gòu)造函數(shù)prototype屬性
function Foo(){
this.x=10
}
Foo.prototype.getX = function(){
console.log(this)//Foo{x:10,getX:function}
console.log(this.x)//10
}
var foo = new Foo();
foo.getX();
在Foo.prototype.getX函數(shù)中唆途,this指向的foo對象富雅,
5.call/apply/bind
當(dāng)一個函數(shù)被call/apply/bind調(diào)用時,this的值就取傳入的對象的值
6箭頭函數(shù)的this
箭頭函數(shù)本身沒有this肛搬,箭頭函數(shù)的內(nèi)部的this是此法作用域没佑,由上下文確定的
9.函數(shù)節(jié)流/函數(shù)防抖
- 函數(shù)防抖:當(dāng)調(diào)用一個動作幾秒后才會執(zhí)行動作,若在這幾秒內(nèi)又調(diào)用動作則重新計算時間執(zhí)行函數(shù)(只有最后一次事件被觸發(fā))
- 函數(shù)節(jié)流:預(yù)先設(shè)置一個執(zhí)行周期滚婉,當(dāng)調(diào)用動作的時刻大于等于執(zhí)行周期則執(zhí)行該動作图筹,然后進入下一個周期(一定時間內(nèi)只觸發(fā)一次)
都是為了限制函數(shù)得執(zhí)行次數(shù),提高性能
1.防抖
//簡單的理解就是在執(zhí)行函數(shù)之前檢查定時器是否已經(jīng)存在
//如果存在說明之前已經(jīng)觸發(fā)了定時器让腹,需要清除之前的定時器
//再重新生成一次定時器远剩,重新執(zhí)行
function debounce(fn,wait){
let timer = null;
let that = this;
return function(){
if(timer){
clearTimeout(timer)
}else{
timer=setTimeout(()=>{
fn.call(that,arguments)
//arguments 是類數(shù)組,指傳入函數(shù)的參數(shù)
},wait)
}
}
}
function fn(){
console.log(1)
}
debounce(fn,1000);
/**
* @desc 函數(shù)防抖
* @param func 函數(shù)
* @param wait 延遲執(zhí)行毫秒數(shù)
* @param immediate true 表立即執(zhí)行骇窍,false 表非立即執(zhí)行 第一次是否立刻觸發(fā)
*/
function debounce(func,wait,immediate) {
var timeout;
return function () {
var context = this;
var args = arguments;
if (timeout) clearTimeout(timeout);
if (immediate) {
var callNow = !timeout;
//記錄timeout是否為空瓜晤,第一次為空所以為true,下面的判斷會立即執(zhí)行腹纳,等待防抖的時間timeout會被再次重置為空痢掠,如果在這個時間范圍內(nèi),callNow會為false,所以不會執(zhí)行下面的判斷
timeout = setTimeout(function(){
timeout = null;
}, wait)
if (callNow) func.apply(context, args)
}
else {
timeout = setTimeout(function(){
func.apply(context, args)
}, wait);
}
}
}
2.節(jié)流
// 簡單的說就是再一個時間范圍執(zhí)行一次嘲恍,不論這個范圍內(nèi)觸發(fā)多少次足画,只執(zhí)行一次
function throttle (fn,delay){
var timer = null;
var startTime = Date.now();
return function(){
var endTime = Date.now();
var remaining = delay - (endTime - startTime);
var context = this;
var args = arguments;
clearTimeout(timer);
if(remaining<=0){
fn.apply(context,args);
startTime = Date.now();
}else{
timer = setTimeout(fn,remaining)
}
}
}
function fn(){
console.log(1)
}
throttle(fn,1000)
10.如何使用正則實現(xiàn)trim()?
function trim(string){
return string.replace(/^\s + | \s + $/g," ")
}
11.手寫AJAX
var xhr = new XMLHttpRequest();
xhr.open('GET',"/XXXX");
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
console.log('請求完成')
if(xhr.response.status>=200 && xhr.response.status<=300){
console.log(' 請求成功')
console.log(xhr.responseText)
}else{
}
}
}
12.數(shù)組去重
function fn (arr){
var temp =[];
for(var i =0;i<arr.length;i++){
if(temp.indexOf(arr[i] )== -1){
temp.push(arr[i])
}
}
return temp
}
fn(arrr)
es6去重
Array.from(new Set(arrr))