1.原型 构造函数 实例
- 原型(prototype):每当定义一个对象(函数也是对象)的时候,对象都会包含一些预定义的属性,其中每个函数对象都包含一个prototype属性,这个属性指向函数的原型对象
- 构造函数:可以通过new 来新建一个对象的函数
- 实例:通过构造函数和new创建出来的对象就是实例,实例通过
_proto_
指向原型,通过constructor指向构造函数 - 每个对象都有
_proto_
属性,但是只有函数对象才有prototype属性
例子:
var obj={}等同于 var obj=new Object();
obj是构造函数(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的构造函数.prototype
因为person1的构造函数 === Person
所以 person1._proto_ === Person.prototype
2.Person._proto_
是什么
因为Person._proto_ === Person的构造函数.prototype
因为Person的构造函数 === Function
所以Person._proto_ ===Function.prototype
3.Person.prototype._proto_
是什么?
因为Person.prototype是一个普通的对象,一个普通对象的构造函数 ===Object
所以 Person.prototype._proto_ ===Object.prototype
4.Object.prototype._proto_
是什么
Object.prototype对象也有_proto_
属性,但是比较特殊,为null,处于原型链的顶端,所以Object.prototype._proto_ ===null
2.原型链
原型链是由原型对象组成,每个对象都有_proto_
属性,指向了创建该对象的构造函数和原型,_proto_
将原型对象链接起来组成了原型链,是用来实现继承和共享属性的有限对象链。
- 属性查找机制:当查找对象属性时,如果实例对象自身不存在该属性,则沿着原型链向上一级查找,找到则输出,如果没找到,则沿着原型链继续向上查找,直到最顶层原型对象Object.prototype,如果还没有则输出undefined
- 属性修改机制:只会修改实例对象本身的属性,如果不存在,则进行添加该属性,如果需要修改原型的属性时,则可以用: b.prototype.x = 2;但是这样会造成所有继承于该对象的实例的属性发生改变。
3.函数柯里化
在一个函数中,首先添加几个固定的参数,在返回一个新的函数,称为函数的柯里化,可以多次重复调用。
ES5实现高阶函数也叫函数的柯里化
function add(x){
return function(y){
return x+y
}
}
var add2= add(2);
add2(3); // => 5
add(10)(11); // => 21
ES6箭头函数实现函数柯里化
const add= x=> y=> x+y;
4.闭包
function init() {
var name = "Mozilla"; // name 是一个被 init 创建的局部变量
function displayName() { // displayName() 是内部函数,一个闭包
alert(name); // 使用了父函数中声明的变量
}
displayName();
}
init();
5.对象的深浅拷贝
对象的拷贝在js中比较常见
- 浅拷贝:仅仅复制对象的引用,而不是对象本身,一旦修改拷贝对象,原对象也会被修改
- 深拷贝:把复制的对象所有引用的对象全部拷贝包括地址,修改拷贝的对象,原对象不会改变。
1.浅拷贝
浅拷贝的方法比较简单,只是简单复制引用,,如果我们要复制对象的属性不引用类型就可以使用浅拷贝,
1.简单的赋值也属于浅拷贝
var obj = { age: '12',name:'jack' };
var newObj = obj;
//此时将obj中的age属性值改变,会发现newObj中的age属性值跟着改变
obj.age = '13'; //obj: { age: '13' } newObj: { age: '13',name:''jack}
2.遍历复制,返回新对象
function shallowCopy(obj) {
var copy = {};
// 只复制可遍历的属性
for (key in obj) {
// 只复制本身拥有的属性
if (obj.hasOwnProperty(key)) {
copy[key] = obj[key];
}
}
return copy;
}
obj.hasOwnProperty() MDN解释
这个方法可以用来检测一个对象是否含有特定的自身属性
语法:obj.hasOwnProperty(prop)
参数:要检测的属性 字符串 名称或者 Symbol
返回值: 用来判断某个对象是否含有指定的属性的 Boolean
3.js内部的浅拷贝
var newObj = Object.assign({}, originObj);其中第一个参数是我们最终复制的目标对象,后面的所有参数是我们的即将复制的源对象,支持对象或数组。
4.数组的浅拷贝
[].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];
// 避免相互引用对象导致死循环,如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];
// 避免相互引用对象导致死循环,如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.代码的复用
当你发现代码多次重复的时候,应该考虑代码的复用。
- 函数封装
- 继承
- 复制extend
7.模块化
模块化开发是现在前端开发的重要部分,模块化提高了项目的可维护性和可拓展性、可协作性,在浏览器中ES6支持模块化,在Node中使用commonjs的模块支持
- es6 :import /export
- commonjs:require /module.exports /exports
- amd: require /defined
1.require支持动态导入,import不支持
2.require是同步导入,import是异步导入
3.require是值拷贝,导出值不会影响导入值,import指向内存地址,导入值会随导出值变化
8.this
1.全局环境/普通函数
在全局函数中,this永远指向window,普通函数的调用,不是构造函数没有使用new,其中this也是指向window
var a = 1;
function fen(){
console.log(this);//window
console.log(this.a)//1
}
fen();
2.构造函数的this指向
构造函数就是指函数new出来的一个对象,
function Fen(){
this.a= 1;
console.log(this)//Fen{a:1}
}
var foo= new Fen();
console.log(foo.a)//10
如果函数作为构造函数使用,其中this就代表他new出来的对象,如果直接调用Fen()函数,而不是new Fen(),那么Fen()就是普通函数,this还是指向全局window
3.对象方法
如果函数作为对象的方法时,那么this指向该对象
var obj = {
a:1,
foo:function(){
console.log(this)//Object
console.log(this.a)//1
}
}
obj.foo();
如果在对象方法中定义函数,
var obj = {
a:1,
foo:function(){
function a(){
console.log(this)//window
console.log(this.a)//undefined
}
a();
}
}
obj.foo();
函数a虽然在obj.foo内部定义,但是依然是一个普通函数,this指向window,如果想调用变量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函数不作为对象方法被调用
var obj = {
a:1,
foo:function(){
console.log(this)//window
console.log(this.a)//undefined
}
}
var fn=obj.foo();
fn();
obj.foo被赋值给一个全局变量,并没有作为obj的一个属性被调用,那么此时this还是指向全局window
4.构造函数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函数中,this指向的foo对象,
5.call/apply/bind
当一个函数被call/apply/bind调用时,this的值就取传入的对象的值
6箭头函数的this
箭头函数本身没有this,箭头函数的内部的this是此法作用域,由上下文确定的
9.函数节流/函数防抖
- 函数防抖:当调用一个动作几秒后才会执行动作,若在这几秒内又调用动作则重新计算时间执行函数(只有最后一次事件被触发)
- 函数节流:预先设置一个执行周期,当调用动作的时刻大于等于执行周期则执行该动作,然后进入下一个周期(一定时间内只触发一次)
都是为了限制函数得执行次数,提高性能
1.防抖
//简单的理解就是在执行函数之前检查定时器是否已经存在
//如果存在说明之前已经触发了定时器,需要清除之前的定时器
//再重新生成一次定时器,重新执行
function debounce(fn,wait){
let timer = null;
let that = this;
return function(){
if(timer){
clearTimeout(timer)
}else{
timer=setTimeout(()=>{
fn.call(that,arguments)
//arguments 是类数组,指传入函数的参数
},wait)
}
}
}
function fn(){
console.log(1)
}
debounce(fn,1000);
/**
* @desc 函数防抖
* @param func 函数
* @param wait 延迟执行毫秒数
* @param immediate true 表立即执行,false 表非立即执行 第一次是否立刻触发
*/
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,下面的判断会立即执行,等待防抖的时间timeout会被再次重置为空,如果在这个时间范围内,callNow会为false,所以不会执行下面的判断
timeout = setTimeout(function(){
timeout = null;
}, wait)
if (callNow) func.apply(context, args)
}
else {
timeout = setTimeout(function(){
func.apply(context, args)
}, wait);
}
}
}
2.节流
// 简单的说就是再一个时间范围执行一次,不论这个范围内触发多少次,只执行一次
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.如何使用正则实现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.数组去重
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))
网友评论