javascript第三周知识点

227 阅读16分钟

一:正则表达式

1、正则表达式的概念:定义字符串中【字符出现规则】的表达式

2、使用场景:切割、替换、验证

3、如何定义正则表达式:

0、语法:/正则表达式/

1、最简单的正则:关键字原文本身:

str=str.replace(/no/ig,"yes");
i:忽略大小写
g:全部

2、备选字符集:一个备选字符集:规定了一位字符可用的备选字符列表

1、语法:/[备选字符列表]/
2、强调:
a、一个中括号,只能匹配一位字符
b、正则默认只要满足后,就不再管理后续操作,后续用户可以乱输入
解决:只要是做验证,必须/^[备选字符集]$/,前加^后加$代表要求从头到尾完全匹配
3、特殊:
a、如果备选字符集中,unicode号是连续,那么中间部分可用-代替
比如:一位数字:[0-9]
一位字母:[A-Za-z]
一位数字、字母、下划线:[0-9A-Za-z_]
一位汉字:[\u4e00-\u9fa5]
b、除了xxx之外,其他都可以:[^xxx] [^0-9]

3、预定义字符集:前辈们提供好了一些常用的字符集的简化写法:

一位数字:\d ==> [0-9]
一位数字、字母、下划线:\w ==> [0-9A-Za-z_]
一位空白字符:\s ==> 空格、制表符、换行
一位除了换行外的任意字符:.

4、量词:规定了一个字符集出现的次数

1、有明确数量
a、字符集{n,m}:前边相邻的字符集,最少出现n次,最多出现m次
b、字符集{n,}:前边相邻的字符集,最少出现n次,多了不限
c、字符集{n}:前边相邻的字符集,必须出现n次
2、没有明确数量
a、字符集?:前边相邻的字符集,可有可无,最多一次
b、字符集*:前边相邻的字符集,可有可无,多了不限
c、字符集+:前边相邻的字符集,至少一次,多了不限

5、指定匹配的位置:

^:以xxx开头
$:以xxx结尾
特殊:如果^$同时出现:前加^后加$,代表要求从头到尾完全匹配 - 只要是做验证必须加

6、选择和分组:

选择:规则1|规则2 - 可以在多个规则中选择满足的规则进行执行
分组:添加子规则:(规则1|规则2)

7、预判:密码强度

(?![0-9]+$)字符集量词- 不能全由数字组成
(?![0-9A-Z]+$)字符集量词 - 不能全由数字组成、不能全由大写组成、不能全由数字和大写的组合组成
(?![a-z0-9]+$)(?![a-zA-Z]+$)(?![0-9A-Z]+$)[0-9A-Za-z]{2,4} - 不能是纯数字、纯大写、纯小写,也不能是小写和数字的组合,也不能是小写和大写的组合,也不能是数字和大写的组合

8、正则对象:

1、创建正则对象:2种方法
a、直接量:var reg=/正则表达式/后缀
b、构造函数:var reg=new RegExp("正则表达式","后缀")
2、正则对象的方法var bool=reg.test(user);
布尔值如果是true,说明用户输入的符合我们的要求,false说明不通过

案例1
<script type="text/javascript">
    var user="Aa0a";
    var reg=/^(?![0-9A-Z]+$)[0-9A-Za-z]{2,4}$/;
    // 2-4位字母和数字的组合,至少一位大写字母和一位数字
    var reg=/^(?![a-z0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{2,4}$/
   // 2-4位字母和数字的组合,三种至少都要有
   var reg=/^(?![a-z0-9]+$)(?![a-zA-Z]+$)(?![0-9A-Z]+$)[0-9A-Za-z]{2,4}$/
   console.log(reg.test(user));
</script>
案例2:简单的注册表单
<body>
	<form id="form" action="" method="get">
		手机号:<input name="phone" />
		<div></div><br>
		密码框:<input name="pwd" />
		<div></div><br>
		邮箱框:<input name="email" />
		<div></div><br>
		<input type="submit" value="注册" />
	</form>
	<script type="text/javascript">
		var inps = document.getElementsByTagName("input");
		for (var i = 0; i < inps.length - 1; i++) {
			inps[i].onfocus = function () {
				if (this.name == "phone") {
					this.nextElementSibling.innerHTML = "手机";
				} else if (this.name == "pwd") {
					this.nextElementSibling.innerHTML = "密码:规则:、";
				} else if (this.name == "email") {
					this.nextElementSibling.innerHTML = "邮箱:规则:、";
				}
				this.nextElementSibling.style.color = "#ccc";
			}
			inps[i].onblur = function () {
				var user = this.value;
				if (this.name == "phone") {
					var reg = /^(\+86|0086)?\s*1[3-9]\d{9}$/;
				} else if (this.name == "pwd") {
					var reg = /^(?![a-z0-9]+$)(?![a-zA-Z]+$)(?![0-9A-Z]+$)[0-9A-Za-z]{2,4}$/
				} else if (this.name == "email") {
					var reg = /^\d{4}$/
				}
				var bool = reg.test(user);
				if (bool) {
					this.nextElementSibling.innerHTML = "成功";
					this.nextElementSibling.style.color = "green";
				} else {
					this.nextElementSibling.innerHTML = "失败";
					this.nextElementSibling.style.color = "red";
				}
			}
		}
		form.onsubmit = function () {
			inps[0].onblur();
			inps[1].onblur();
			inps[2].onblur();
			for (var i = 0; i < inps.length - 1; i++) {
				if (inps[i].nextElementSibling.style.color == "red") {
					return false;
				}
			}
		}
	</script>
</body>

二:字符串中支持正则表达式的API:分割、替换:

1、分割字符串:

var arr=str.split(reg);

分割字符串的案例
<script type="text/javascript">
			var str="tom&jerry#mary%jack@rose";
			var arr=str.split(/[&#%@]/);
			console.log(arr);
</script>

2、替换字符串:

1、基本替换法:

var newStr=str.replace(reg,"新内容");
特殊:a、默认只会替换第一个关键字,想要替换所有记得加上后缀g
b、替换的内容只能是一个固定的新内容

基本替换法的案例
var str="我可,沃克,蜗壳,我科,窝可可";
var newStr=str.replace(/[我沃窝蜗][可克壳科]+/g,"**");
console.log(newStr);

2、高级替换法

高级替换法的案例
var str="我可,沃克,蜗壳,我科,窝可可";
var newStr=str.replace(/[我沃窝蜗][可克壳科]+/g,function(a){
    console.log(a);//正则匹配到的关键字
    console.log(b);//正则匹配到的关键字的下标
    console.log(c);//原文本身
    return a.length==2?"**":a.length==3?"***":"****";
});
console.log(newStr);

3、格式化:如果替换API使用正则时,并且里面带有分组,会得到更多的形参

var newStr=str.replace(reg,function(a,b,c...){
console.log(a);//正则匹配到的关键字
console.log(b);//第一个分组获取到的内容
console.log(c);//第二个分组获取到的内容
return "格式化的东西"
});
格式化案例:根据身份证输出出生年月日
var str="500103198602215933";	
var newStr=str.replace(/(\d{6})(\d{4})(\d{2})(\d{2})(\d{4})/,function(a,b,c,d,e,f,g,h){
    console.log(a);//正则匹配到的关键字
    console.log(b);//是第一个分组获取到的内容
    console.log(c);//是第2个分组获取到的内容
    console.log(d);//是第3个分组获取到的内容
    console.log(e);//下标
    console.log(f);//原文
    return c+"年"+d+"月"+e+"日";
})
console.log(newStr);
格式化案例:将英文单词的首字母全部改为大写
var str="Lorem ipsum dolor sit amet, consectetur adipisicing elit. Beatae eum reiciendis nihil impedit.";
var newStr=str.replace(/(\w)(\w*)/g,function(a,b,c){
    return b.toUpperCase()+c;
})
console.log(newStr);
console.log(str);
格式化案例:去掉一串字符前面和后面的空格
var str="		hello world		";
str=str.replace(/^\s+|\s+$/g,"");
console.log(str);

三、Math:专门提供了数学计算的API

强调:不能创建,不需要创建,直接使用!

1、唯一的属性:Math.PI

2、API:

1、取整:3种

a、上取整:Math.ceil(num) 只要超过一点点,就会取下一个整数,此方法小数位数不能超过15位否则会失效;
b、下取整:Math.floor(num) 不管超过多少,都会省略掉小数部分,此方法小数位数不能超过15位否则会失效;
c、四舍五入取整:Math.round(num);
问题:四舍五入虽然不错,但是以上三个API只能取整
解决:parseFloat(num.toFixed(d));//既有四舍五入功能,又具有保留自定义小数位数的操作,但结果是一个字符串
笔试题:封装一个函数,实现可以自定义保留小数位数并且四舍五入的功能,但是不允许使用toFixed

笔试题:封装一个函数,实现可以自定义保留小数位数并且四舍五入的功能,但是不允许使用toFixed
function round(num,d){
	num*=Math.pow(10,d);
	num=Math.round(num);
	num/=Math.pow(10,d);
	return num.toString();
}

2、乘方和开方

乘方:Math.pow(底数,幂); - 简化连续的乘法
开方:Math.sqrt(num); - 只能开平方

3、最大值和最小值

语法:Math.max/min(a,b,c,d,e,f....);
获取到最大的一个数或者最小的一个数
问题:不支持数组参数
解决:Math.max/min.apply(Math,arr);
apply:自己没有的方法可以去借用,可以将数组打散为单个参数悄悄进行传入

4、绝对值:将负数转为整数

Math.abs(num);

5、随机数:只要页面上具有随机的功能,底层一定用到了随机数

Math.random() 已经是一个随机数了,随机的小数0-1,有可能取到0,但是绝对不可能取到1
公式:parseInt(Math.random()*(max-min+1)+min);

案例:机选双色球
<body id="bd">
	<button id="btn">机选双色球</button>
	<script type="text/javascript">
		btn.onclick = function () {
			var user = prompt("请问要购买几注");
			var mains = document.getElementsByTagName("main");//[elem,elem,elem]
			if (mains.length > 0) {
				for (var i = mains.length - 1; i >= 0; i--) {
					mains[i].remove();
				}
			}
			for (var j = 0; j < user; j++) {
				var reds = [];
				while (reds.length < 6) {
					var r = parseInt(Math.random() * (33 - 1 + 1) + 1);
					if (reds.indexOf(r) == -1) {
						reds.push(r);
					}
				}
				reds.sort(function (a, b) { return a - b })
				var blue = parseInt(Math.random() * (16 - 1 + 1) + 1);
				var father = document.createElement("main");
				for (var i = 0; i < reds.length; i++) {
					var red = document.createElement("div");
					red.innerHTML = reds[i];
					red.className = "red";
					father.appendChild(red);
				}
				var bl = document.createElement("div");
				bl.innerHTML = blue;
				bl.className = "blue";
				father.appendChild(bl);
				bd.appendChild(father);
			}
		}
	</script>
</body>

四、Date:封装了一个日期对象,提供了对日期事件进行操作的API

何时使用:以后只要网页上跟事件日期相关的,我们都要使用date对象

1、创建日期对象:5种

1、创建当前时间:

var now=new Date();

2、创建自定义时间:

var birth=new Date("yyyy/MM/dd hh:mm:ss");

3、创建自定义时间:

var birth=new Date(yyyy,MM,dd,hh,mm,ss);
缺点:月份需要修正:计算机中月份是从0开始到11的

4、复制一个日期对象:

为什么:日期对象的API都是直接修改原日期对象,使用API后,无法同时保存住旧的日期对象
何时使用:在调用日期对象的API之前都要先复制,在使用API
语法:var end=new Date(now);

5、var xxx=new Date(毫秒数);

计算器其实保存的就是从1970年1月1日至今的毫秒数

2、操作:

1、两个日期对象之间可以相减,得到毫秒差,换算出你想要的任何一部分,看出细节:其实日期对象保存的就是一个毫秒数

2、API:

分量:时间单位 FullYear Month Date Day Hours Minutes Seconds

1、每一个分量都有一对儿方法:getXXX()/setXXX()

特殊:1、取值范围:
年:当前年份
月:0-11
日:1-31
时:0-23
分:0-59
星期:0-6,0代表星期天
2、星期只有get,没有set

2、固定套路:对着某个日期直接做加减

date.setXXX(date.getXXX()+num)

3、格式化为字符串:

国际化日期格式化为本地化日期:date.toLocaleString();
缺点1、具有浏览器的兼容性问题 2、则不可以再使用日期对象的API,也不会再带有进制操作了
好处:转为了字符串可用字符串的API
解决:自定义format格式化

案例:创建Date对象保存员工的入职时间,合同有效期为3年,合同到期前需要提前一个月续签,如果提前一个月的时间刚好是周末则需要提前到前一个星期的周五。要在续签时间前一周向员工发出续签提醒
<script type="text/javascript">
		var start = new Date("2018/4/13 7:45:45");
		var end = new Date(start);
		end.setFullYear(end.getFullYear() + 3);
		var renew = new Date(end);
		renew.setMonth(renew.getMonth() - 1);
		if (renew.getDay() == 6) {
			renew.setDate(renew.getDate() - 1);
		} else if (renew.getDay() == 0) {
			renew.setDate(renew.getDate() - 2);
		}
		var alert = new Date(renew);
		alert.setDate(alert.getDate() - 7);
		console.log(format(start));
		console.log(format(end));
		console.log(format(renew));
		console.log(format(alert));
		document.write("<h1>" + format(start).slice(11, 16) + "</h1>")
		function format(date) {
			var y = date.getFullYear();
			var m = date.getMonth() + 1;
			var d = date.getDate();
			var h = date.getHours();
			h < 10 && (h = "0" + h);
			var am = h >= 12 ? "下午" : "上午";
			var M = date.getMinutes();
			M < 10 && (M = "0" + M);
			var s = date.getSeconds();
			s < 10 && (s = "0" + s);
			var arr = ["天", "一", "二", "三", "四", "五", "六"]
			var day = date.getDay();
			return y + "年" + m + "月" + d + "日 星期" + arr[day] + am + " " + h + ":" + M + ":" + s;
		}
	</script>

五、Error:错误对象:

1、浏览器自带4种错误类型:

语法错误:SyntaxError - 多半都是哪里的符号写错了
引用错误:ReferenceError - 根本就没有创建过,就去使用了
类型错误:TypeError - 不是你的方法,你却使用了
范围错误:RangeError - 只有一个API:num.toFixed(d);//d的范围只是0-100之间

2、错误处理:当程序发生错误时,保证程序不会异常中断的机制

为什么:只要报错,就会导致后续代码终止(闪退),用户体验感降低

语法:try...catch...语句
	    try{
		可能出错的代码
	    }catch(err){
		console.log(err);//提示用户错误的原因是什么
	    }

后续代码都可以执行到
不推荐:try...catch...的执行效率非常的低下,更推荐的是if...else

3、抛出自定义错误:

throw new Error("自定义错误信息");

案例:老IE不支持e对象错误处理
btn.onclick = function (e) {
    try {
    console.log(e.screenX);
    } catch (err) {
    console.log("老IE不支持e对象")
    }
    if (e) {//undefined - 老IE
    console.log(e.screenX);
    } else {
    console.log("老IE不支持e对象")
    }
    console.log("后续代码");
  }
案例:如果传入的不是一个数字,或者不能被隐式转为一个数字,则会报错
function round(num, d) {
    if (!isNaN(num) && !isNaN(d)) {
        num *= Math.pow(10, d);
        num = Math.round(num);
        num /= Math.pow(10, d);
        return num
    } else {
        throw new Error("必须要放入数字");
    }
}

六、Function:函数对象 - 方法

1、创建:3种

1、声明方式

function 函数名(形参列表){函数体;return 结果;}
具有完整的声明提前

2、直接量方式

var 函数名=function(形参列表){函数体;return 结果;}
也有声明提前,但是赋值留在原地

3、构造函数方式 - 函数体不是固定的

var 函数名=new Function("形参1","形参2",...,"函数体");
函数体是动态拼接时候,注意不能省略""部分

2、调用:

函数名();

3、函数的执行原理:

1、程序加载时:

创建执行环境栈(ECS):保存函数调用顺序的数组
首先压入全局执行环境(全局EC)
全局EC中引用着全局对象window
window中保存着全局变量

2、定义函数时:

创建函数对象:封装函数的定义
在函数对象中创建scope属性,记录着自己来自的作用域
全局函数的scope都是window

3、调用函数前

在执行环境栈ECS压入新的函数的EC
创建活动对象AO:保存着本次函数调用时用到的局部变量
在EC中添加scope chain属性引用AO
设置AO的parent属性为函数的scope引用的对象

4、调用时:

变量的使用规则:优先使用局部的,局部没有才找全局,全局没有才报错

5、调用完:

函数的EC会出栈,AO会自动释放,局部变量也就自动释放了

4、闭包:目的:保护一个可以反复使用的局部变量的一种词法结构

结合了全局和局部的优点
唯一的缺点:受保护的变量永远不能释放,用多了会导致内存泄漏,尽量的少用,只有一个点会用到那就是防抖节流

1、使用方法:3步

1、创建一个外层函数
2、在其中创建一个受保护的局部变量 3、外层函数调用要返回内层函数,此内层函数在操作受保护的变量

固定语法:
   function 外层函数(){
	受保护的变量;
	return function(){
	不断的操作受保护的变量
	return 结果;
	}
    }	
   var 内层函数=外层函数();

2、注意:

1、判断闭包,找到受保护的变量,确定其值
2、外层函数调用几次,就创建了几个闭包,受保护的变量就有了几个副本
3、同一次外层函数调用,返回的内层函数,都是在使用同一个受保护的变量

3、正式开发:防抖节流:减少DOM树的渲染,DOM数据渲染的次数越频繁页面的效率越底下

3个事件需要去做防抖节流:
1、elem.onmousemove - 鼠标移动事件,每次移动就会触发
2、input.oninput - input每次修改内容就会触发
3、window.onresize - 屏幕每次改变大小就会触发

固定公式:
function fdjl(){
    var timer=null;
    return function(){//判断有没有定时器,如果有定时器就清除定时器
    if(timer){clearTimeout(timer)}
    timer=setTimeout(function(){
	//你要做的操作
    },毫秒数)
    console.log(timer);//定时器的序号
    }
}
var animate=fdjl();
案例:鼠标移动防抖节流
<body>
	<div id="d1">1</div>
	<script type="text/javascript">
		d1.onmousemove = function () {
			animate();
		}
		function fdjl() {
			var timer = null;
			return function () {
				//判断有没有定时器,如果有定时器就清除定时器
				if (timer) { clearTimeout(timer) }
				timer = setTimeout(function () {
					d1.innerHTML = parseInt(d1.innerHTML) + 1;
				}, 3000)
				console.log(timer);//定时器的序号
			}
		}
		var animate = fdjl();	
	</script>
</body>

案例:input每次修改内容防抖节流
<body>
	<input id="inp" />
	<script type="text/javascript">
		inp.oninput = function () {
			animate();
		}
		function fdjl() {
			var timer = null;//3
			return function () {
				//判断有没有定时器,如果有定时器就清除定时器
				if (timer) { clearTimeout(timer) }
				timer = setTimeout(function () {
					console.log(inp.value);
				}, 1000)
			}
		}
		var animate = fdjl();
	</script>
</body>

案例:屏幕每次改变大小防抖节流
<body>
	<div id="d1">啦啦啦</div>
	<script type="text/javascript">
		onresize = function () {//JS版本的媒体查询
			console.log(1);
			animate();
		}
		function fdjl() {
			var timer = null;//3
			return function () {
				//判断有没有定时器,如果有定时器就清除定时器
				if (timer) { clearTimeout(timer) }
				timer = setTimeout(function () {
					if (innerWidth >= 768) {
						d1.style.background = "red";
					} else {
						d1.style.background = "blue";
					}
				}, 1000)
			}
		}
		var animate = fdjl();
	</script>
</body>

5、面试题:

1、如何创建函数:

2、作用域:

全局(成员哪里都可以使用)、函数(成员只能在函数调用时内部可用)
变量的使用规则:优先使用局部的,局部没有找全局,全局没有就报错

3、声明提前:

在程序正式执行之前,会将var声明的变量和function声明的函数集中提前到当前作用域的顶部,但是赋值留在原地,变量比函数轻

4、按值传递:

如果传递的是原始类型,互不影响。如果传递的是引用类型,相互影响。

5、重载:

相同的函数,根据传入的实参的不同,会自动选择对应的函数执行
a、为什么:减少程序员的负担
b、问题:JS不支持重载,JS不允许同时存在多个同名函数,如果存在,最后的一个会覆盖之前的所有函数
c、解决:arguments对象 - 只能在函数内部可用,是一个类数组对象:
d、作用:接住所有的实参,以后甚至不用形参都可以了
e、只有3点相同:
1、支持下标
2、支持length
3、支持遍历
f、变相实现重载:通过判断arguments的不同,执行不同的操作

6、匿名函数:理解概念

没有名字的函数,没有变量名/函数名引用着,调用完毕后会立马释放 - 一次性函数 分为2种:
1、匿名函数自调:代替全局作用域

(function(){
	函数体;  
})()

好处:调用完毕会立刻释放
2、匿名函数回调:某个函数调用时,传入的实参又是一个函数,而且不需要我们调用自动执行

比如:
arr.sort(function(a,b){return a-b})
str.replace(reg,function(){})

注意:匿名函数,不是自调,就一定是回调

7、作用域链scope chain:

以EC中的scope chain属性为起点,经过AO逐级引用,形成的一条链式结构,就称之为叫做作用域链
作用:查找变量

七:Object,面向对象开发方式 - 三大特点(封装、继承、多态)

1、什么是面向对象:在程序中都使用对象来描述现实中的一个事物

现实中事物的属性,代码中就成为了对象的属性
现实中事物的方法,代码中就成为了对象的函数
现实中所有数据都必须包含在一个事物中才有具体的意义

2、封装/创建/定义/声明/实例化:自定义创建对象:3种

1、直接量方式:

var obj={
    "属性名":属性值,
     ...
    "方法名":function(){},
    ...
}

强调:1、其实属性名和方法名的""可以省略,但是不推荐,以后JSON必须加""
2、如何访问对象属性和方法

obj.属性名 === obj["属性名"]
obj.方法名(); === obj["方法名"]();

强烈建议:使用.去访问
3、访问到不存在的属性:返回undefined
4、也可以在后续随时随提的添加自己想要的东西
5、特殊:this在当前对象的方法内,指向的当前调用方法对象
只要以后对象的方法想要使用对象自己的属性,那么就写为this.属性名

2、预定义构造函数:

var obj=new Object();//空对象
obj.属性名=属性值;
...
 obj.方法名=function(){}
...

以上两个方法仅适合创建单个对象,如果想要创建多个对象则太繁琐

3、自定义构造函数:2步

1、创建一个构造函数
function 类名(形参,...){
	this.属性名=形参1;
	this.属性名=形参2;
	...
}
2、反复调用构造函数创建出多个对象
var xxx=new 类名(实参,...)

3、继承:父对象的成员(属性和方法):子对象可以直接使用

1、为什么继承:

代码重用!节约内存空间!

2、何时继承:

只要多个子对象公用的属性和方法,都应该集中定义在父对象中

3、JS的面向对象是基于原型(父对象)的

4、什么是原型:

保存一类子对象共有属性和共有方法的原型对象(父对象)

5、如何去找到原型对象:

1、对象名.__proto__ - 至少要创建一个对象才可以使用
2、构造函数名.prototype
new 构造函数(Object RegExp Date Function String Number Boolean...)

6、在原型对象中添加共有属性和共有方法

原型对象.属性名=属性值
原型对象.方法名=function(){}

7、每一个对象都有一个.__proto__的属性指向着自己的原型

每一个构造函数都有一个.prototype属性指向着自己的原型

8、自有和共有

自有:保存在对象本地的
共有:保存在原型对象中的,子对象都可以直接使用

4、多态:子对象觉得父对象提供的方法不好用,可以再本地定义一个同名成员,优先使用离自己更近的方法

同一个函数名,但根本不是同一个方法

5、面试题:

1、聊一聊你了解面向对象和面向过程的开发方式?

a、面向过程:开始->经过->结束,我们一直使用的开发方式。
b、面向对象:对象:属性和方法
所有的属性和方法全都是包含在一个对象中的,比如:轮播对象、选项卡对象...
优:1、有技术
2、所有的操作都包含在一个对象中,显得更有意义
3、维护时非常的舒服
4、铁索连舟 - 一个方法调用,触发了一大堆操作
缺:1、难度大 - this的指向

2、this指向:

1、单个元素绑定事件 this->单个元素
2、多个元素绑定事件 this->当前触发的元素
3、函数中也可以使用 this->当前调用函数的对象
4、构造函数中如果出现了this this->当前正在创建的对象

3:两链一包:

原型链:自己没有的属性和方法,可以顺着原型链一直向上找,直到最顶层:Object.prototype - 万物皆对象
作用:查找属性和方法

6、笔试题

1、判断一个属性是自有还是共有:

1、判断自有:obj.hasOwnProperty("属性名");
如果结果为true,说明是自有
如果结果为false,可能是共有也可能是没有

2、判断共有:2个条件

obj.hasOwnProperty("属性名")==false;//可能是共有也可能是没有
"属性名" in obj;//in关键字会查找自己的原型
if(obj.hasOwnProperty("属性名")==false && "属性名" in obj){
	console.log("共有")
}else{
	console.log("没有")
}

完整的:
if(obj.hasOwnProperty("属性名")){
     console.log("自有");
}else{
    if(obj.hasOwnProperty("属性名")==false && "属性名" in obj){
	console.log("共有")
    }else{
	console.log("没有")
    }
}
案例:判断自有还是共有
                        var ws={
				"gf":"一堆",
				"phone":"iphone13",
				"dog":"阿拉斯加"
			}
			ws.__proto__.money=1000000000000;
			if(ws.hasOwnProperty("js")){
				console.log("自有")
			}else{
				if(ws.hasOwnProperty("js")==false && "js" in wsc){
					console.log("共有")
				}else{
					console.log("没有")
				}
			}

2、修改/删除属性

自有:修改:obj.属性名=新值;
删除:delete obj.属性名;

共有:修改:千万不要在本地做操作,那会导致在本地添加上一个同名属性,优先使用自己的,但并没有修改原型对象
删除:千万不要在本地做操作,那会导致白做没有任何效果
强调:一定要找到原型再做操作

                        var ws={
				"gf":"一堆",
				"phone":"iphone13",
				"dog":"阿拉斯加"
			}
			var ws1={
				"gf":"一堆",
				"phone":"iphone13",
				"dog":"阿拉斯加"
			}			
			ws.__proto__.money=1000000000000;
			ws.__proto__.money=1;
			delete ws.money;

3、为一类人添加方法:

为一类人添加共有方法固定写法
			构造函数名.prototype.函数名=function(){
				this->函数中的代表当前调用此函数的对象
			}
案例:最常见的一道题:为老IE的数组添加indexOf方法 - 原本只有字符串可以使用,是后续升级数组才能使用的
			if(Array.prototype.indexOf === undefined){//老IE
				Array.prototype.indexOf = function(key,starti){
					starti===undefined&&(starti=0);
					for(var i=starti;i<this.length;i++){
						if(this[i]==key){
							return i;
						}
					}
					return -1;
				}
			}
案例:为字符串添加排序
                    String.prototype.sort=function(){
				var arr=this.split("");
				arr.sort();
				return arr.join("");
			}	
			var str="helloworld";
			var str1="dshajkqweyui123";

4、判断x是不是一个数组:4种

1、判断当前x对象是否是继承自Array.prototype的
Array.prototype.isPrototypeOf(x);
true说明是一个数组

2、判断当前x对象是否是由此构造函数所创建
x instanceof Array
true说明是一个数组

3、Array.isArray(x); - 只有数组才有此方法
true说明是一个数组

4、根据多态:子对象觉得父对象提供的方法不好用,可以再本地定义一个同名成员,优先使用离自己更近的方法 同一个函数名,但根本不是同一个方法
在Object的prototype中保存着最原始的toString方法
原始的toString输出的结果:[object 构造函数名]

固定套路:
if(Object.prototype.toString.apply(arr)==="[object Array]"){
      consoloe.log("是数组")
}

5、如何设置自定义继承

设置单个对象的继承:obj.__proto__=新对象

案例:设置单个对象的继承
                <script type="text/javascript">
			var ws={
				"gf":"一堆",
				"phone":"iphone13",
				"dog":"阿拉斯加",
			}	
			var wjllll={
				"money":1000000000,
			}	
			var dy={
				"js":300
			}
			ws.__proto__=wjllll;
			wjllll.__proto__=dy;
			console.log(ws.js);
			console.log(wjllll);
		</script>

设置多个对象的继承:构造函数名.prototype=新对象
注意时机:在创建对象之前就设置好父对象

案例:设置对个对象的继承
            <script type="text/javascript">
			function classpeople(name,age){
				this.name=name;
				this.age=age;
			}	
			var dy={"js":300};	
			var jwj=new classpeople("啊啊啊",18);
			var lyz=new classpeople("不不不",19);
			var zt=new classpeople("冲冲冲",20);
			classpeople.prototype=dy;
			var lsr=new classpeople("对对对",20);
			console.log(jwj);
			console.log(lyz);
			console.log(zt);
			console.log(lsr);
		</script>