javaScript高级

JS高级

1、面向对象(170213)

面向对象的三大特性:封装,继承,多态;

	tips:JS没有多态;

2、封装函数

传统封装函数的方式:
	function fun_1(){
	
	}
		封装函数会出现的问题:1、全局变量污染;
							2、代码结构混乱,不利于维护;

构造函数:
	function Person(){
		this.name = name;
		this.age = age;
		this.sayHi = function (){};
	}
	var p = new Person;
	var p1 = new Person;

		tips:在这里,如果将方法放在了构造函数内部,
		每一次利用构造函数创建对象,都需要开辟一个新空间保存该方法,
		并且这些方法都是相同的,这造成了资源的浪费;

		tips:因此,我们将创建出来的对象需要用到的方法都放在prototype里;
		每一个利用该构造函数创建出来的实例化对象都会指向同一个prototype;

		tips:如果构造函数没有return,其调用的值为undefined;
			eg:
				var pig = new Pig();	——→这样创建出了一个实例化对象,对象名叫做pig;
				var pig2 = Pig();	——→这样只是把构造函数当函数调用,函数的返回值赋值给pig2;

prototype:
	1、构造函数.prototype可以访问到原型;
	2、创建出来的实例化对象.__proto__可以访问到原型;
	3、原型也可以使用.constructor方法访问到原型的构造函数;
	4、实例化.constructor可以访问到构造函数,但是构造函数无法访问到实例化对象;

	5、原型里面一般存放的是实例化对象都要公用到的方法;
	构造函数里面存放的是对于新创建出来的对象的属性初始化的操作;
		
		tips:尽量将所有的功能都写在原型函数中,便于实例化对象使用;
			在构造函数中仅调用必要的功能,且在原型函数内的功能可以进行分类,在构造函数仅执行总类功能,细类封装在原型中;

	6、每一个新创建出来的对象,如果调用它的方法,首先将从它本身的属性中寻找该方法;
	如果该实例化对象的属性中没有这个方法,将会向其prototype中寻找;

	7、当构造函数的原型在中途被替换成了其他对象时,这时候,已经创建的对象的原型不会改变,
	但是在这之后创建的对象的原型都会指向这个替换过后的对象;

	8、使用对象的动态特性,可以给原型对象添加成员;
		eg:
			Person.prototype.name = "xxx";
			Person.prototype["name"] = "xxx";
				tips:两种方法都适用;

		tips:一个对象的属性和方法统称为成员;

3、原型图终极版

输入图片说明

4、继承(170214)

(1)、混入式继承(mix-in)
		使用for-in循环,将对象1的属性赋值给对象2;
	eg:
		for(var key in obj_1){
			obj_2[key] = obj_1[key];
		}

	tips:通过混入式继承只能给单一对象实现继承;
		要让创建出的所有对象都拥有某个对象的属性,用混入式将会很麻烦;

(2)、原型继承(prototype)
		要让构造函数的原型拥有某个对象的属性, 有两种方法:
			1、直接将该构造函数的原型指定为该对象;
			2、将该对象的属性循环地添加到原型中,推荐使用该方法;

(3)、经典继承
		var obj_2 = Object.create(obj_1);
			创建一个obj_2,令obj_1作为obj的原型;

(4)、应用:安全地使用内置对象

		如果将需要的方法都放在原型中,原型的结构会变成混乱,
		而且所有人都把方法放在原型里面,多人操作会造成方法冲突。
		因此需要加入新的原型继承,使之能够安全地使用内置对象;
			eg:
				var arr = new MyArray();
				MyArray.prototype = [];
				
			tips:这样就在arr和内置对象Array的中间插入了一个自定义的原型MyArray;
			我们可以将我们需要的方法存放在自定义的原型中, 这样创建的数组对象仍然可以获取来自内置对象Array的方法;

5、Object.prototype成员

(1)、hasOwnProperty("属性名");
		eg:
			obj.hasOwnProperty(key);
				判断obj对象中是否存在属性key;

(2)、isPrototypeOf
		eg:
			obj_1.isPrototypeOf(obj_2);
				判断obj_1是否是obj_2的原型;

(3)、propertyIsEnumerable
		eg:
			obj.propertyIsEnumerable("属性名");
				判断在obj中该属性是否可以通过枚举得到,
			
			tips:这个属性必须是该对象自身拥有的,而非从prototype引用;

(4)、toString()
	toLocaleString()
		二者都是转换成字符串的方法,LocalString显示为本地区相关的格式;

	tips:一个非常明显的区别就是Date对象显示出来的日期;

(5)、valueOf()
	array.valueOf()仍然是一个数组;
	object.valueOf()仍然是一个对象;

6、function成员

(1)、length:表示函数的形参个数
(2)、name
		匿名函数的name是anonymous;
(3)、prototype:
		当函数作为构造函数存在的时候,prototype就指向了这个构造函数的原型;
(4)、caller
		函数在哪个函数内部调用,那么caller就指向这个外部的函数,
		如果是在全局调用的,就是null;

		eg:
			function Person(a, b){
	            console.log(Person.caller);
	        }
	        function f1(){
	            Person(1,2,3);
	        }
	        f1();

(5)、__proto__:
		当函数作为实例化对象存在的时候,__proto__就指向了这个实例化对象的原型;

7、Function对象

(1)、
	var fn1 = new Function(arg1, arg2, arg3, ..., argN, body);
		body写函数内部的代码,arg1指该函数的形参;
		这时候,就创建出了一个名为fn1的函数。

	eg:
		var func = new Function(){"num", "console.log(num);"};

	tips:Function是一个构造函数,这个构造函数既可以看作是构造函数,也可以看作是Function(即自身)的实例化对象;
		任何函数的构造函数都是Function;

(2)、instance of
	判断构造函数的原型是否在对象的原型链上,返回值为true或false;
	eg:
		obj instanceof Object;		//obj为实例化对象或构造函数

		tips:任何对象 instanceof Object,其返回值都为true;	

(3)、函数的三种创建方式
	eg:
		var fn = new Function();
		var fn = function (){};
		function fn(){}

		tips:函数加了括号是调用,没有加括号是赋值;	

8、eval函数

(1)、Evaluate,将字符串解析成js代码;
	eg:	
		eval("var a = 10;");
			这段字符串使用了eval方法后,将会创建一个变量a,赋值为10;

(2)、将JSON格式的字符串转换成js对象
		1、JSON.parse(json)字符串,返回值为js对象,有兼容问题;
		2、json2.js插件,引入该插件即可解决;
		3、使用eval函数将JSON格式的字符串转换成对象;

		tips:使用{}可以进行代码块划分,从而使代码结构更清晰;
			eg:
				{
					var a = 10;
				}

			在使用eval方法时,为了避免json字符串的首尾的{}解析成了代码块,
			eg:
				var obj = eval("(" + JSON字符串 + ")");
				或
				eval("var obj = " + str);

	tips:Function 和 eval都可以将字符串转换成代码,但是不推荐使用这种方式,
	这种方式具有安全性问题(例如XSS跨站脚本攻击);

9、静态成员和实例成员

(1)、静态成员:通过构造函数去访问的成员,即在new的时候构造函数提供的成员;
(2)、实例成员:通过实例去访问的成员,即实例自身有的属性;

10、arguments对象

arguments对象里面存储了所有传入函数内部的实参;

属性:
	(1)、length,可表示传入实参的个数;
	(2)、callee,表示当前arguments对象所在的函数;
		eg:
			function test(a, b){
	            console.log(arguments.callee);
	        }
	        test(1, "a");
		
			tips:这时候在控制台显示的为text()函数的内容;

11、递归(170216)

函数自己调用自己,即为递归;
(1)、递归的两个条件
		1、在函数内部自己调用了自己;
		2、函数具有结束条件,结束条件达到时递归结束;

			tips:递归必须有结束条件,没有结束条件的递归是死循环;

(2)、递归的效率极低,十分耗费资源,一般要配合缓存使用;

(3)、eg:

	求前n项和:
		function sum(n){
            if(n == 1){
                return 1;
            }
            return sum(n - 1) + n;
        }
		tips:在最小的时候出现的值,即n==1的时候,结束条件达成需要return的值;
			1+2+3+4
			1+2+3
			1+2
			1
		所以n==1的时候return 1;

	求n的阶乘:
		function fct(n){
            if(n == 1){
                return 1;
            }
            return fct(n - 1) * n;
        }
		tips:	1*2*3*4*5
				1*2*3*4
				1*2*3
				1*2
				1
		所以当n==1的时候return 1;

12、作用域与变量提升

(1)、在js中,有且只有函数可以创建作用域;
		和词法作用域对应的作用关于叫做动态作用域;
		js中的作用域是词法作用域;
		js不是动态作用域;

	tips:若为动态作用域,会考虑到函数的调用环境,来判断其变量提升;

(2)、js执行分为两阶段
		1、预解析,变量提升(hoisting),function和var进行提升;
		2、代码执行阶段;

	tips:在if语句内部的var会进行变量提升,但是赋值操作不会;	

	eg:
		function test(a){
			console.log(typeof a);
			var a = 123;
		}
		test(123);
	tips:在函数中,传入的实参,会在函数内部进行一个var 形参 = 实参的操作,先于所有的函数代码之前;
		这里调用了函数,传入了实参,执行的顺序为:
		test(123);
		var a = 123;
		var a ;
		console.log(typeof a);
		a = 123;

(3)、在function中出现的变量,若未在函数内部声明,则向上一级的作用域寻找定义了的变量进行赋值。
	以此类推,直到找到了该变量的var,一直到全局作用域仍为定义,则报错。

tips:if(undefined),为false;
		if(0),为false;
		if(null),为false;
		if(" "),为false;
tips:元素中的属性,可以直接用点方法进行调用。
	如,body.id、body.class。

13、作用域链与变量搜索原则(170217)

(1)、绘制作用域链的步骤:

	1、看整个全局是一条链, 即顶级链, 记为 0 级链;	
	2、看全局作用域中, 有什么变量和函数声明, 就以方格的形式绘制到 0 级练上
	3、再找函数, 只有函数可以限制作用域, 因此从函数中引入新链, 标记为 1 级链
	4、然后在每一个 1 级链中再次往复刚才的行为

	
(2)、变量的访问规则
	
	1、首先看变量在第几条链上, 在该链上看是否有变量的定义与赋值, 如果有直接使用
	2、如果没有到上一级链上找( n - 1 级链 ), 如果有直接用, 停止继续查找.
	3、如果还没有再次往上刚找... 直到全局链( 0 级 ), 还没有就是 is not defined
	
	tips:注意,同级的链不可混合查找


(3)、
	Foo.get,表示调用了在构造函数Foo中的get属性;
		Foo().get,先执行了Foo()函数,调用其返回值的get属性;

		tips:函数加了括号则为调用;
		tips:构造函数如果当做普通函数来调用,其内部this为window,
			return什么就是什么,没有return返回undefined;	

	new Foo.getName(); 
		直接计算Foo.getName属性的值;

    new Foo().getName(); 
		new Foo(),创建了一个Foo的实例化对象,在该对象里面调用其getName方法,如果该对象本身不具备该方法则向原型查找;

    new new Foo().getName(); 
		与上例类似;

		tips:new和一个(),中间的部分即为构造函数,new本身并不对结果产生影响;

14、闭包(170217)

(1)、闭包(Closure),在函数内部创建新的函数,用于修改函数内部定义的变量,
在外部函数return一个内部函数,这样就实现了对函数内部变量的操作;
概念:指可以访问独立变量的函数;

(2)、闭包作用:1、解决全局变量污染的问题;
				2、保护变量,可以对变量赋值做一些校验;

(3)、闭包的缺陷:定义的函数因为返回值一直处于被调用状态,因此资源无法被自动释放,内存一直被占用,造成了资源浪费;
	如果能不用闭包就尽量不用;
		
(4)、应用
	eg:
		for(var i = 0; i < 10; i++){
            setTimeout(function(j){
                //var j = i
                function inner(){
                    console.log(j);
                }
                return inner;
            }(i), 1000);
        }

	tips:在js中存在事件队列的说法,for循环的事件队列要优先于setTimeout,
	因此如果不用闭包,setTimeout的功能将无法正常进行;

	tips:js中的任务分为主要任务和次要任务;
		主要任务(for, var ,function ...etc);
		次要任务(setTimeout,setInterval ...etc);

	eg:
		function func(){
            var num = Math.random();
            var obj = {
                setNum: function(value){
                    num = value;
                },
                getNum: function(){
                    return num;
                }
            }
            return obj;
        }
	tips:在return的时候返回了一个对象,对象里面存储了内部函数,通过调用这些内部函数方法,可以对函数中的变量进行操作;、

15、缓存

浏览器缓存
CDN,Content Delivery Network,内容分发网络;
硬件缓存(RAM,内存);

代码实现缓存:
	用一个数组或者对象存储数据,在发生了繁琐重复操作时有限从缓存数组中查找结果,缓存没有结果再作计算;

eg:
	利用缓存和闭包,对递归的优化,以斐波那契数列为例。
		function createFib(n){
            var arr = [];	//充当缓存的数组
            function fib(n){
                var num = arr[n];
				//判断在缓存数组里是否有这个数,有则直接用,没有就计算;
                if(!num){
                    if(n == 1 || n == 2){
                        num = 1;
                    }else{
                        num = fib(n - 1) + fib(n - 2);
                    }
                    arr[n] = num;
                }
                return num;
            }
            return fib(n);
        }
		createFib(5);

16、沙箱模式(170219)

(1)、特征:在沙箱内部的操作,不会对外界产生影响;
(2)、沙箱的基本模型:自调用函数(IIFE,Immediately Invoked Function Expression);
	eg:
		(function(){
			var a = 10;
		})()

(3)、自调用函数的多种写法:只要能作为一个句子存在,并进行了自调用即可;
	(function (){})();
	(function (){}());
	+ function (){}();
	! function (){}();

(4)、向外暴露接口操作
	(function (w){
		window.jQuery = window.$ = jQuery;
	})(window)

		tips:	1、传参的意义在于,实现逻辑上的隔离;
				2、代码压缩时,其实就是对变量重命名,但window对象和其他内置对象并不能重命名,
			这时候如果直接使用window对象会不利于代码压缩,所以需要自定义形参来接收window对象;
(5)、沙箱模式的应用:
		1、框架封装;
		2、组件;
		3、插件;

17、调用模式

1、函数调用模式;
	function fn(){
		在function中的this指window对象;
	}

2、对象调用模式;
	var obj = {
		sayHi:function(){

		}
	}
	obj.sayHi();

3、构造函数调用模式;
	var obj = new Object();

4、上下文(context)调用模式;
	lvalue	左值;
	rvalue	右值;(右值不可作为左值赋值)
	两种方式:	1、apply;
				2、call;
	function.apply(obj , [a,b,c]);
	function.call(obj , a, b, c, d ...);

		tips:obj为该函数希望this指向的对象,a,b,c,d等为传入函数的实参;
		tips:两者区别在于传参的形式不同,
			apply在参数不确定的时候使用,call在参数个数确定的时候使用;
		tips:apply的参数都放在了数组里,这个特点使得apply很常用;

		如果obj传入的类型为简单数据类型, 会自动转换成复杂数据类型;
	eg:
		test.apply(undefined);
		test.apply(null);		this会指向window;
		
	eg:
		将伪数组变为真数组
			var fakeArr = {
				length:3,
				0:a,
				1:b,
				2:c
			}
			var realArr = [];
			realArr.push.apply(realArr , fakeArr);
			//这时候,fakeArr的数据内容将会存储到realArr中,变成了真数组;
		tips:核心用法就是apply会自动将数组拆开,并将其作为参数来调用方法;

	eg:
		function Animal(){}
		function Dog(){
			Animal.call(this);
		}
		var d = new Dog();
		console.log(d);

		tips:在Dog函数的内部,使用call方法将Animal的this指向实例化对象;

18、创建对象的模式

(1)、工厂模式
	eg:
		function createPerson(name, age){
	        var obj = {};
	        obj.name = name;
	        obj.age = age;
	        console.log(this);
	        return obj;
	    }
	    var p = createPerson("张学友", 50);
	    var p1 = createPerson("郭富城", 50);

	tips:工厂模式的特点为,类似于构造函数,但是工厂模式的函数内部自建了一个对象,通过传递参数给对象赋值,最后将该对象作为返回值返回;

(2)、构造函数模式(constructor)
	eg:
		function Person(name, age){
            this.name = name;
            this.age = age;
        }
        var p = new Person("王力宏", 30);

(3)、寄生模式
	eg:
		function createPerson(name, age){
            var obj = {};
            obj.name = name;
            obj.age = age;
            console.log(this);
            return obj;
        }
        var p = new createPerson("刘德华", 60);
	tips:寄生模式和工厂模式的区别在于,创建对象的方式寄生模式用new方法,工厂模式给函数直接传参;

19、数组遍历(forEach和map)

(1)、forEach
	arr.forEach(value , index , arr){};

	tips:value为数组元素,index为元素索引,arr为当前遍历的数组(一般不加);

(2)、map(映射)
	arr.map(value , index , arr){};

	tips:map的不同之处在于map有返回值,在其中所做的操作返回的结果都会存到一个新的数组,这个新的数组会作为返回值return;

	eg:
		var arr = [a,b,c,d];
		var strArr = arr.map(String);
			tips:最后的结果strArr将会是以字符串组成数组;

20、严格模式(170220)

"use strict"
'use strict'

	1、在严格模式下声明变量 var 不可以省略;
	2、在严格模式下形参名不允许重复;
	3、早期严格模式下,对象的属性名是不可以重复的,
    但是在ECMAScript 6中,支持了这种写法!
	4、八进制在严格模式下不允许使用;
		eg:
			var a = 010;
	        console.log(a);

		tips:a赋值的时候首位为0表明该数为八进制;

	5、在严格模式下,eval函数具有自己的作用域
        eg:
			eval("var a = 10; console.log(a)");
	        console.log(a);

21、Object.defineProperty

Object.defineProperty(需要添加属性的对象 , "属性名" , {
	writable:true,
	enumerable:true,
	configurable:true,
	value:"属性值";
})

	(1)、可写性(writable): 默认为false,不能被赋值
			value 设置属性的值;

        · setter 和 getter
	            如果只有setter 只写属性  只能赋值,不能获取值
	            如果只有getter 只读属性  只能读取,不能赋值

	        tips:setter 和 getter一般不和writable还有 value一起使用;
	            getter 和 setter可以做校验数据的操作!

			tips:意即是说,有了writable和value就不会有setter和getter;
			tips:在用getter和setter时,写成get或set;

    (2)、可遍历性(enumerable): 默认为false, 不能被遍历;
    (3)、可删除性(configurable): 默认为false,不能被删除;
    
	eg:
		Object.defineProperty(obj, "job", {
            enumerable: true,
            configurable: true,
            get:function () {
                return jobValue;
            },
            set:function(value){
                jobValue = value;
            }
        });

	eg:
		Object.defineProperty(obj, "job", {
            writable: true,
            enumerable: true,
            configurable: true,
            value: "singer",
        });

22、面向对象编程细节

1、面向对象编程, 主要分为两块,第一部分为构造函数,第二部分为原型;

2、在构造函数中存放一些对于对象初始化的操作,对象需要用到的方法尽量保存到原型中;
3、构造函数初始化时调用必要的方法对实例化对象进行初始化,执行代码可以写成一个总类,细类写在原型中,原型提供总类初始化的方法;
	eg:
		在构造函数中只写一个this.init();
		在原型中,init方法调用当前原型的其他方法,从而实现了调用方法的封装;
4、在面向对象封装完毕后,构造函数和原型可以用沙箱模式进行封装,这样创建出来的函数不会对外界产生影响,安全性较高;

5、在写原型的时候,要手动的将原型的constructor指向自定义的构造函数;

5、创建出来的沙箱模式也需要给外界提供接口,便于在外界调用;
	eg:
		(function (w){
			function jQuery(){}
			jQuery.prototype = {
				constructor:jQuery, 	//——→手动将构造函数指向jQuery
				init:function (){
					this.add();
					this.push();
				},
				add:function(){},
				push:function(){}
			}
			window.jQuery = window.$ = jQuery;
		})(window)

转载于:https://my.oschina.net/zhongjunhui/blog/857929

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值