对象
用已学的知识点,描述一下你心目中的对象
var mrDeng = {
name : "mrDeng",
age : 40,
sex : "male",
health : 100,//可在控制台更改
smoke : function () {
console.log('I am smoking');
mrDeng.health --;
},
drink : function () {
console.log('I am drink');
mrDeng.health ++;
}
}
改进 第一人称:this
var mrDeng = {
name : "mrDeng", 千万别是等于号
age : 40,
sex : "male",
health : 100,//可在控制台更改
smoke : function () {
console.log('I am smoking');
this.health --;
},
drink : function () {
console.log('I am drink');
this.health ++;
}
}
1.属性的增、删、改、查
增
drink:function(){
console.log('I am drinking');
this.health ++;
}
meDeng.wife = "xiaowang";
// console.log(mrDeng.wife = "xiaoliu");
// console.log(mrDeng);
改
console.log(mrDeng);
console.log(mrDeng.sex = "male");
console.log(mrDeng.sex);
删 delete + 属性
当一个变量没有声明就是用报错,对象的属性没有就访问打印 undefined,不会报错
var deng = {
prepareWife : "xiaowang",
name : "laodeng",
sex : "male",
gf : "xiaoliu",
wife : "",
divorce : function () {
delete this.wife;
this.gf = this.PrepareWife;
},
getMarried : function () {
this.wife = this.gf;
},
changePrepareWife : function (someone){
this.PrepareWife = someone;
}
}
2.对象的创建方法
1.var obj = {} 叫 plainObject 对象字面量/对象直接量
2.构造函数
(1) 系统自带的构造函数 new Object
var obj = new Object();
obj.name = 'abc';
obj.sex = 'female';
obj.say = function(){
}
var obj = {
name : ""
}
系统提供的:new Object();Array();Number();Boolean();Date();
(2) 自定义
function Car(){//方便使用-人为的,构造函数特点:大驼峰式命名规则,只要是单词,首字母大写(小驼峰式:第一个外的首字母大写)
this.name = "BMW";
this.height = "1400";
this.lang = "4900";
this.weight = 1000;
this.health = 100;
this.run = function (){
this.health --;
}
}
var car = new Car();//每个都一样,但是每个都独立
var car1 = new Car();//不是一个人,不互通
console.log(car.run());
console.log(car1.health);
console.log(car.health);
demo
实现自己选配颜色
function Car(color){
this.color = color;
this.name = "BMW";
this.height = "1400";
this.lang = "4900";
this.weight = 1000;
this.health = 100;
this.run = function (){
this.health --;
}
}
var car = new Car('red');
var car1 = new Car('green');
console.log(car/car1);
function Student(name,age,sex) {
this.name = name;
this.age = age;
this.sex = sex;
this.grade = 2017;
}
var student = new Student('zhangsan',18,'male');
注意事项
var obj = new Object(){
obj.name = 'abc';
obj.sex = 'female';
obj.say = function(){
}
}
var obj = {
name : ""//冒号
}
构造函数内部原理
有 new 则发生三步
1.在函数体最前面隐式的加上 var this = {} 2.执行 this.xxx = xxx; 3.隐式的返回 this
function Student(name,age,sex) {
//var this = {
// name : ""
// age :
// };
this.name = name;
this.age = age;
this.sex = sex;
this.grade = 2017;
// return this;
}
var student = new Student('zhangsan',18,'male');
function Person(name, height){
// var this = {}
this.name = name;
this.height = height;
this.say = function (){
console.log(this.say);
}
// return this;
}
console.log(new Person('xiaoliu',180).name);
模拟构造函数
function Person(name, height){
var that = {};
that.name = name;
that.height = height;
return that;
}
var person = new Person('xiaowang', 180);
var person1 = new Person('xiaozhang',175);
冷门知识
function Person(name, height){
var this = {};
this.name = name;
this.height = height;
this.say = function (){
console.log(this.say);
}
return {};//显示返回空对象,so person很person1都返回空对象
// return this;
}
var person = new Person('xiaowang', 180);
var person1 = new Person('xiaozhang',175);
// 但是return一个原始值不允许
Object.create(原型)方法
包装类
1.小知识
原始值不能有属性和方法,只有对象能有,对象包括对象自己,数组,function。
数字不一定是原始值。数字分两种:原始值数字才是原始值。数字,字符串分为两种。
var num = 123;数字
Var num = new number(123);也数字,对象 123
console.log(num);
console.log(num.abc='a');
console.log(num.abc);
console.log(num);
console.log(num*2);//成了数字,没有了对象属性,同理字符串,布尔
不能有属性的两个原始值:undefined null
String();
Boolean();
Number();
var num = new Number(123);
var str = new String('abcd');
var bol = new Boolean('true');
// undefined与null不可以有属性
console.log(num.abc);
2.现象:
var str = “abcd”;
str.length = 4;//理论上不可以
str.abc = ‘a’;
str.abc = undefined;
原始值不可能有属性和方法,因为经历了一个过程——包装类,才能调用
var num = 4;//包装类
console.log(num.len = 3);
//new Number(4).len = 3; delete
// new Number(4).len
console.len(num.len);//undefined
// num没有length
考题
基于一个理论:数组截断
var arr = [a,b,c,d];
console.log(arr.length = 2);
console.log(arr);
成哥真题
var str = "abcd";
str.length = 2;
// new String('abcd').length = 2;delete
console.log(str);//abcd
变式
var str = "abcd";
str.length = 2;
// new String('abcd').length = 2;delete
// new String('abcd').length
console.log(str.length);//4
原形
1.定义
原型是 function 对象的一个属性,它定义了构造函数制造出的对象的公共祖先。通过该构造函数产生的对象,可以继承该原型的属性和方法。原型也是对象。
构造函数产生的对象:
// Person.prototype = {} 是祖先
Person.prototype.name = "hehe";
function Person() {
}
var person = new Person();
应用:
实例一:自己有取自己的
Person.prototype.LastName = "Deng";
Person.prototype.say = function(){
console.log('hehe');
}
function Person() {
// this.LastName = 'ji';先看自己后看父亲
}
var person = new Person();
var person1 = new Person();
实例二:
Person.prototype.LastName = "Deng";
Person.prototype.say = function(){
console.log('hehe');
}
function Person(name,age,sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
var person = new Person('xuming',30,'male');
2.提取共有属性。
function Car(color,owner) {
this.owner = owner;
this.carName = "BMW";//每次生产都得执行这三句,耦合
this.height = 1400;
this.lang = 8900;
this.color = color;
}
var car = new Car('red','prof.ji');
优化
Car.prototype.height = 1400;
Car.prototype.lang = 8900;
Car.prototype.carName = "BMW";
function Car(color,owner) {
this.owner = owner;
this.color = color;
}
var car = new Car('red','prof.ji');
var car1 = new Car('green','laodeng');
3.原形的增删改查
增
Person.prototype.lastName = "Deng";//原形属性没有改,想改,就要调用Person.prototyoe.lastName
function Person(name) {
this.name = name;
}
var person = new Person('xuming');
console.log(person.lastName="james");//这不叫修改,这叫增加
console.log(person);
console.log(person.lastName);
删
console.log(delete person.name);
console.log(person.name);
console.log(delete person.lastName);
console.log(person.lastName);//删除失效
优化
Car.prototype.height = 1400;
Car.prototype.lang = 8900;
Car.prototype.carName = "BMW";
一步到位
Car.prototype = {
height : 1400,
lang : 4900,
carName : "BMW"
}
改
2.对象如何查看对象的构造函数 — > constructor 构造器
function Person(){
}
//可以手动更改
//构造器constructor:谁生的
Car.prototype = {
constructor : Person
}
function Car(){
}
var car = new Car();
console.log(car.constructor);
查
- 对象如何查看原型 — > 隐式属性 proto
function Person() {}
var person = new Person(); //浅紫色,隐式命名规则
console.log(person);
person.prototype.name = 'abc';
function Person(){
}
var person = new Person();
// 私人属性:var __pravite
console.log(person.__proto__)//__proto__里面放的是原形
Person.prototype.name = 'abc';
function Person(){
var this = {
// __proto__: Person.prototype
}
}//先看看自己有没有name属性,没有的话沿着proto指向找
var perosn = new Person();
console.log(perosn.name);
改变 proto 指向会使得指向更改
Person.prototype.name = 'abc';
function Person() {
// var this = {
// // __proto__: Person.prototype
// }
}
var obj = {
name: "sunny"
}
var person = new Person();//换爹
person.__proto__ = obj;
演示 1
Person.prototype.name = "sunny";
function Person() {
}
var person = new Person();
Person.prototype.name = "cherry";
person.name;
分析:自己没有,找proto,是Person.prototype:sunny,最后又改成cherry
演示 2
Person.prototype.name = "sunny";
function Person() {
}
Person.prototype.name = "cherry";
var person = new Person();
console.log(person.name);
同理分析cherry
演示 3
Person.prototype.name = "sunny";
function Person() {
}
var person = new Person();
Person.prototype = {
name : "cherry"
}
person.name;
答案:sunny。
.的写法是原有的基础上把原有的值改了,这一次是把原形改了,换了个新对象
简化:引用值的互相赋值
var obj = {name : "a"};
var obj1 = obj;
obj = {name : "b"};
obj1:a,obj:b
Person.Prototype.name = "sunny";
function Person() {
//var this = {__proto__:Person.prototype}
}//proto指向不变还是sunny
var person = new Person();
Person.prototype = {//把自己空间换了
name:'cherry'
}
// 更加简化
// Perosn.prototype = {name:"a"};
// __proto__ = Person.Prototype;
// Person.Prototype = {name:"b"};
演示 4
Person.prototype.name = "sunny";
function Person() {
}
Person.prototype = {
name: "cherry"
}
var person = new Person();
person.name;
答案:cherry
预编译:函数 function Person() { } 提升到最上面,然后顺序执行,执行到最后一行,有new了,
就有function Person() { var this = __proto__ Person.prototype } ,再访问:下面的把上覆盖
原型链引入
Grand.prototype.LastName = "Deng";
function Gand(){
}
var grand = new Grand();
Father.prototype = grand;
function Father(){
this.name = "xuming";
}
var father = new Father();
Son.prototype = father;
function Son(){
this.hobbit = "somke";
}
var son = new Son();
console.log(son.hobbit);
console.log(son.name);//顺着链找
console.log(son.toString);
Grand.prototype.__proto__=Object.prototype是所有对象的最终原形
console.log(Object.prototype);
console.log(Object.prototype.__proto__);proto没了,so就是终端
原型链
如何构成原型链?
1.原型链上属性的增删改查
增,删,修改:只有本人有权限,子孙没有
console.log(delete Father.prototype.n);//删不了
特例:引用值调用修改
var grand = new Grand();
Father.prototype = grand;
function Father() {
this.name = 'xuming';
this.fortune = {
card1 : 'visa'
}
}
var father = new Father();
Son.prototype = father;
function Son(){
this.hobbit = "somoke";
}
var son = new Son();
console.log(son.fortune);
son.fortune = 200;
console.log(son);
console.log(father.fortune);
son.fortune.card2 = 'master';
cons.log(father);
demo
Grand.prototype.LastName = "Deng";
function Grand(){
}
var grand = new Grand();
Father.prototype = grand;
function Father(){
this.name = "xuming";
this.forture = {
card1 : 'visa'
};
this.num = 100;
}
var father = new Father();
Son.prototype = father;
function Son(){
this.hobbit = "smoke";
}
var son = new Son();
console.log(son.num++);100
console.log(father);100
console.log(son.num);101
演示
Person.prototype = {
name : "a",
sayName : function(){
console.log(this.name);
}
}
function Person(){
}
var person = new Person();//答案:a
Person.prototype = {
name : "a",
sayName : function(){
console.log(this.name);
}
}
function Person(){
this.name = "b";
}
var person = new Person();
小常识:
a.sayName()
sayName 里面的 this 指向是,谁调用的这个方法,this 就指向谁
person.sayName(); person 调用的,b
如果 Person.prototype.sayName(); a
Person.prototype = {
height : 100
}
function Person(){
this.eat = function(){
this.height ++;
}
}
var person = new Person();
console.log(person.eat);
person.eat();
console.log(person.eat);//默認return:undefined 查看要写代码
公司规范:数组,对象都用字面量创建
var obj = {};//对象自变量创建形式,有原形
//与var obj1 = new Object();相同
var obj = {};内部来一个new Object()
所以要写对象字面量
绝大多数对象的最终都会继承自 Object.prototype
(选择题真题)
例外:由于 Object.create
console.log(Object.create());//报错,不写代码
console.log(Object.create(null));
Object.create(原型)也能创造对象
// var obj = Object(原形);
var obj = {name : "sunny", age : 123};
var obj1 = Object.create(obj);
// obj1成为了对象,obj1的原形是obj,所以obj1.name就是obj.name
Person.prototype.name = "sunny";
function Person() {
}
var person = Object.create(Person.prototype);
2.关于 toString:
只有 undefined 与 null 不能调用 toString
数字可以,因为经过包装类一层层访问 123.toString();
undefined 没有包装类,是个原始值,没有原形,不能调用
console.log(undefined.toString);
console.log(null.toString);
obj.__proto__ = { name: sunny};
console.log(obj.name);//自己加原形不管用
各个变量各个属性值调用 toString 返回结果不一样 变成字符串
var num = 123;
console.log(num.toString);//字符串
console.log(123.toString);//识别成浮点型
现象
var obj = {};
obj.toString();---->[object Object]
原因
var num = 123;
// num.toString();-->new Number(num).toString();
Number.prototype.toString = function (){
}
// Number.prototype.__proto__ = Object.prototype
重写笔试题:写一个方法重写形式,写一个和系统同样的名,不同功能
// Object.prototype.toString = function (){
// }
Person.prototype = {
toString : function () {
return 'hehhe';
}
}
function Person () {
}
var Person = new Person();
本集多看弱项
3.小 bug
console.log(0.4*100);//js精度不准
console.log(Math.ceil(123.234));//向上取整
console.log(Math.floor(123.99999));//下
随机生成 0-100 随机数
Math.random();//随机数生成函数(0,1)
for(var i = 0;i < 10; i++){
var num = Math.random().toFixed(2)*100;
console.log(num);//会出现偏差
}
解决:先*100 后取整
for(var i = 0; i < 10; i++){
var num = Math.floor(Math.random() * 100);
console.log(num);
}
// 总结:可正常那个计算的范围:小数点前16后16
call/apply 必考
1.call
作用,改变 this 指向。借用别人的函数实现自己的功能
区别,后面传的参数形式不同。
call 需要把实参按照形参的个数传出去 apply 需要传一个 arguments
function test(){
}
test()===test.call();//后面隐式默认
call 里面可以传东西
function Person(name, age){
this.name = name;
this.age = age
}
var person = new Person('deng',100);
var obj = {
}
// Person.call(obj);
// 会让Person里所有的this变成obj 即this=obj
// 怎么传参:Person.call(obj,'cheng',300);
Person.call(obj,'cheng',300);
企业级开发
//开发讲究快准狠,A写的代码能实现B的功能,省时
function Person(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
function Student(name, age, sex, tel, grade){
this.name = name;
this.age = age;
this.sex = sex;
this.tel = tel;
this.grade = grade;//后面覆盖前面
}
// call 借用别人的函数实线自己的功能
function Person(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
function Student(name, age, sex, tel, grade){
// var this = {name : "", age : "",sex : ""}
Person.call(this, name, age, sex);
this.tel = tel;
this.grade = grade;
}
var student = new Student('sunny',123,'male',139,2017);
造车举例
function Wheel(wheelSize,style) {
this.style = style;
this.wheelSize = wheelSize;
}
function Sit(c,sitColor) {
this.c = c;
this.sitColor = sitColor;
}
function Model(height,width,len) {
this.height = height;
this.width = width;
this.len = len;
}
function Car(wheelSize, style, c, sitColor, height, width, len) {
Wheel.call(this, wheelSize,style);
Sit.call(this,c,sitColor);
Model.call(this, height, width, len);
}
var car = new Car(100,'花里胡哨的','真皮','red',1800,1900,4900);
2.apply:
apply 只能传送一个数组形式的实参
Wheel.apply(this, [wheelSize,style]);
总结:
call:需要把实参按照形参的个数传进去
apply: 需要传一个 arguments
作业:电子书 js 设计模式 0-35 页上部分
笔试题:用友 2017 校招前端
继承发展史
1.传统形式
过多的继承了没用的属性
Grand.prototype.lastName = "Ji";
function Grand() {
}
var grand = new Grand();
Father.prototype = grand;
function Father() {
this.name = "hehe";
}
var father = new Father();
Son.prototype = father;
function Son() {
}
var son = new Son();//一系列从头到尾继承,导致不想继续的也继承了
2.借用构造函数
不能继承借用构造函数的原型
每次构造函数都要多走一个函数实际浪费效率
function Person(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
}//实际上call apply不是继承
function Student(name, age, sex, grade) {
Person.call(this,name,age,sex);
this.grade = grade;
}
var student = new Student();
3.共享原型
不能随便改动自己的原型
Father.prototype.lastName = "Deng";
function Father(){
// Son想继承father的prototype
}
function Son(){
}
实现方法
原型链
function Son()上面放上 var father…
共有原形
Father.prototype.lastName = "Deng";
function Father() {
}
function Son() {
}
Son.prototype = Father.prototype
var son = new Son();
var father = new Father();
抽象成函数
Father.prototype.lastName = "Deng";
function Father() {
}
function Son() {
}
function inherit(Target, Origin) {
Target.prototype = Origin.prototype;
}
inherit(Son, Father);
var son = new Son();//son.LastName = "Deng";
要先继承后使用,Son.lastName 指向原来的空间
Father.prototype.lastName = "Deng";
function Father() {
}
function Son() {
}
function inherit(Target, Origin) {
Target.prototype = Origin.prototype;
}
var son = new Son();
inherit(Son, Father); //先继承,后该原形不管用 son.lastName = undefined
不足:
son 给自己多加一个属性,方便后续生产出的对象使用,不能个性实现,继承了,但是影响
Father.prototype.lastName = "Deng";
function Father(){
}
function Son(){
}
function inherit(Target, Origin){
Target.prototype = Origin.prototype;
}
inherit(Son, Father);
Son.prototype.sex = "male";//与father的prototype一致,一个变,都变
Father.prototype
var son = new Son();
var father = new Father();
// 要实现:想继承但不相互影响
4.圣杯模式
通过原型链
function inherit(Target, Origin) {
function F(){};
F.prototype = Origin.prototype;
Target.prototype = new F();
}
Father.prototype.lastName = "Deng";
function Father(){
}
function Son() {
}
inherit(Son,Father);
var son = new Son;
var father = new Father();
console.log(Son.prototype.sex = "male");
console.log(son.sex);
console.log(father.sex);
console.log(Father.prototype);
但是,constructor 应该指向构造函数,然而,son.constructor = ƒ Father()????怎么回事?
原形上系统自带的一个属性叫 constructor,默认指向他的构造函数
son.__proto__ --->new F().__proto__--->Father.prototype
//指向紊乱了
实现继承:必会圣杯模式
function inherit(Target, Origin){
function F(){};
F.prototype = Origin.prototype;
Target.prototype = new F();
Target.prototype.constructor = Target;
Target.prototype.uber = Origin.prototype;//超级父级:超类
}
Father.prototype.lastName = "Deng";
function Father(){
}
function Son(){
}
inherit(Son, Father);
var son = new Son();
var father = new Father();
颠倒
F.prototype = Origin.prototype;
Target.prototype = new F();//不能颠倒,一定要在new之前改原形
类雅虎
闭包的作用:可以实现封装,属性私有化
function Deng(name, wife){
var prepareWife = "xiaozhang";
this.name = name;
this.wife = wife;
this.divorce = function(){
this.wife = prepareWife;
}
this.changePrepareWife = function(target){
prepareWife = target;
}
this.sayPreparewife = function(){
console.log(prepareWife);
}
}
var deng = new Deng('deng','xiaoliu');
console.log(deng.sayPreparewife());
console.log(deng.Preparewife)//直接看看不到 即私有化变量
F 变成了私有化变量
var inherit = (function(){
var F = function(){};//F放在闭包,私有化变量
return function(Target,Origin){
F.prototype = Origin.prototype;
Target.prototype = new F();
Target.prototype.constuctor = Target;
Target.prototype.uber = Origin.prototype;
}
}());
离散数学(重要)——>CS 专业(计算机专业),,,,概率论,人工智能
命名空间
就是对象
管理变量,防止污染全局,适用于模块化开发
一个页面,多人协作,合并冲突
1.老办法:命名空间
var org = {
department1 : {
jicheng : {
name : "abc";
age : 123;
},
xuming : {
}
},
department2 : {
zhangsan : {
},
lisi : {
}
}
}
var jicheng = org.department1.jicheng;
jicheng.name;
2.新方法:闭包私有化变量
webpack
闭包(实现变量私有化)+立即执行函数
var name = "bac";
var init = (function(){
var name = "abc";
function callName(){
console.log(name);
}
return function(){
callName();
}
}())
init();
协作也不冲突
var name = "bac";
var init = (function(){
var name = "abc";
function callName(){
console.log(name);
}
return function(){
callName();
}
}())
var initDeng = (function(){
var name = 123;
function callName(){
console.log(name);
}
return function(){
callName();
}
}())
如何实现链式调用模式(模仿 jquery)
obj.eat().smoke().drink().eat().sleep();
$('div').css('background-color','red').width(100).height(100).
html(123).css('position','absolute').
css('left','100px').css('top','100px');
模拟 jQuery 实现连续调用方法
var deng = {
smoke : function () {
console.log('Smoking...xuan cool!!!');
},
drink : function () {
console.log('Drinking...good');
},
perm : function(){
console.log('perming...cool');
}
}
deng.smoke();
deng.drink();
deng.perm();//怎么实现JQ一样连续调用
为什么不能连续调用
console.log('Smoking...xuan cool!!!');
// return undefined;
方法:
var deng = {
smoke : function () {
console.log('Smoking...xuan cool!!!');
return this;
},
drink : function () {
console.log('Drinking...good');
return this;
},
perm : function(){
console.log('perming...cool');
return this;
}
}
deng.smoke().drink().perm().smoke().drink();
查看属性
想实现 num 为几,就叫哪个媳妇
var deng = {
wife1 : {name : "xiaoliu"},
wife2 : {name : "xiaozhang"},
wife3 : {name : "xiaowang"},
wife4 : {name : "xiaoli"},
sayWife : function(num) {
switch(num) {
case 1:
return this.wife1;
}
}
}
把上述代码简化:变量拼接属性名
var obj = {
name : "abc"
}
**obj.name—->obj[‘name’];**内部会转换成方括号,so 这样写也对
方括号可以字符串拼接
功能实现
var deng = {
wife1: { name: "xiaoliu" },
wife2: { name: "xiaozhang" },
wife3: { name: "xiaowang" },
wife4: { name: "xiaoli" },
sayWife: function (num) {
switch (num) {
case 1:
return this["wife" + num]; //字符串加啥都是字符串
}
},
};
console.log(deng.sayWife(1));
对象的枚举
for in 循环
数据组的遍历:想知道十个人的信息,挨个知道
var arr = [1,3,3,4,5,6,7,8,9];
//遍历 枚举 enumeration
for(var i = 0; i < arr.length; i++){
console.log(arr[i]);//遍历
}
如果想查找(遍历)对象的呢(不知道啥类型,编辑器角度)
var obj = {
name : "123",
age : 123,
sex : "male",
height : 180,
weight : 75
//prop:123
}
for(var prop in obj){
console.log(obj.prop);//不好使---obj.prop--->obj['prop']当成属性了
}
正解:
var obj = {
name : '13',
age : 123,
sex : "male",
height : 180,
weight : 178,
__proto__ : {
lastName : "deng"//也会把原形东西拿出来,不想把原形拿出来————hasOwnProperty
}
}
for(var prop in obj) {
console.log(obj[prop]);
//console.log(obj['prop']);//跟obj.prop一样,这样也不对
}
实现 obj1 里面属性+1 返回
错误示范
var obj1 = {
a : 123,
b : 234,
c : 456
}
var key;
for(key in obj1) {//这样写也行
obj1.key ++;
}
1.hasOwnProperty(重点)
过滤性方法
判断这个属性是否是自己的(不是原形链的)
true
false
不想把原形拿出来 demo
var obj = {
name : '13',
age : 123,
sex : "male",
height : 180,
weight : 178,
__proto__ : {
lastName : "deng"
}
}
for(var prop in obj) {
if(obj.hasOwnProperty(prop)){
console.log(obj[prop]);
}
}
自己设的一定能打印,系统设的一定不能
var obj = {
name : '13',
age : 123,
sex : "male",
height : 180,
weight : 178,
__proto__ : {
lastName : "deng",
__proto__ : Object.prototype//一旦延展的原型链的最顶端,不会打印最顶端
}
}
2.in
和 hasOwnProperty 类似
var obj = {
name : '13',
age : 123,
sex : "male",
height : 180,
weight : 178,
__proto__ : {
lastName : "deng"
}
}
//console.log(height in obj)//height叫变量
console.log('height' in obj)//height这个属性名存不存在于obj
concole.log('lastName' in obj)
和 hasOwnProperty 的区别:
In 判断能不能访问到这个属性或者原形(只能判断对象上能不能访问到这个属性,也可以在父级找)
hasOwnProperty 判断属性属不属于这个对象
开发从来不用
3.instanceof(重点)
function Person(){
}
var person = new Person();
var obj = {};
// A对象是不是B构造函数构造出来的
console.log(person instanceof Person);
// ---->ture
看 A 对象的的原型链上有么有 B 的原形(重点)
console.log(person instanceof Object);
---->ture
console.log([] instanceof Array);
---->ture
console.log([] instanceof Object);
---->ture
console.log(person instanceof Array);
---->false
console.log(obj instanceof Person);
---->false //两个没关系
解决的问题
判断变量是数组还是对象(机器识别)
typeof([])
"object"
typeof({})
"object"
//需求:
var arr = [] || {};
第一种方法:
var obj = {};//var obj = [];
obj.constructor//直接区分出来了
第二种方法:
[] instanceof Array //true
var obj = {};
obj instanceof Array //false
第三种方法:toString
Object.prototype.toString.call([]);
//Object.prototype.toString = function (){
//谁调用,this就是谁
//识别this:
//返回相应结果
//}
// obj.toString();
// call.toString();
Object.prototype.toString.call([]);//数组会替换this
Object.prototype.toString.call(123);
Object.prototype.toString.call({});//区别数组和对象
控制台验证
Object.prototype.toString.call([]);
"[object Array]"
Object.prototype.toString.call({})
"[object Object]"
Object.prototype.toString.call(123);
"[object Number]"
this
1.函数预编译过程 this —> window
function test(c) {
//var this = Object.create(test.prototype);
//相当于
// {
// __proto__ : test.prototype
// }
var a = 123;
function b(){}
}
//AO{
// arguments : [1],//自带的
// this : window,//自带的
// c : 1,
// a : undefined,
// b : function() {}
//}
test(1);
new test();//new导致this指向发生了改变,不在是window了
验证
function test(){
console.log(this);
}
test();
2.全局作用域里 this —> window
console.log(window);
3.call/apply 可以改变函数运行时 this 指向
4.obj.f(); f()里面的 this 指向 obj)
谁调用的方法,this 就指向谁,如果没人调用这个方法,空执行,this 是 window
var obj = {
a : function () {
console.log(this.name)//谁调用,就用谁的name
},
name : 'abc'
}
obj.a();//obj调用
5.this 题
条件判断为假的情况有: 0 、false 、’’ 、 null 、undefined 、未定义对象。
函数声明写在运算符中,其为 true,但 放在运算符中的函数声明在执行阶段时找不到的 。
另外,对未声明的变量执行 typeof 不会报错,会返回 undefined
this 一马平川题
var name = "222";
var a = {
name : "111",
say : function(){
console.log(this.name);
}
}
var fun = a.say;//a.say代表function的函数引用,fun在全局上执行,相当于函数在全局执行
fun()//222全局执行也没人调用
a.say()//111
var b = {
name : "333",
say : function(fun){
//this---->b
fun();//不是this.fun(),谁也没调用,预编译,上面得this--->window,222
}
}
b.say(a.say);//a.say---是上面的函数体, 222
b.say = a.say;//a.say上面的函数拷贝到b.say(代替b.say)
b.say();//333
arguments
arguments.callee
func.caller
arguments
function test(){
console.log(arguments.callee);
}
test();//返回自己的函数体
应用:
var num = (function(n){
if(n == 1) {
return 1;
}
return n*阶乘(n-1);
}(100))
立即执行函数的阶乘:只能用 callee 做
var num = (function(n){
if(n == 1) {
return 1;
}
return n*arguments.callee(n-1);
}(100))
在那个函数里面,就打印哪个
function test(){
console.log(arguments.callee);
function demo(){
console.log(arguments.callee);
}
demo();
}
caller: demo 在哪个环境调用的
function test () {
demo();
}
function demo() {
console.log(demo.caller);
}
test();
克隆
1.浅层克隆
var obj = {
name : 'abc',
age : 123,
sex : 'female'
}
var obj1 = {}
function clone(origin,target){
for(var prop in origin){
target[prop] = origin[prop];
}
}
clone(obj,obj1);
//打印obj1显示已经copy过去了
实现容错:
var obj = {
name : 'abc',
age : 123,
sex : 'female'
}
var obj1 = {}
function clone(origin,target){
var target = target || {};
for(var prop in origin){
target[prop] = origin[prop];
}
return target;
}
clone(obj,obj1);
原始值没问题,但是引用值拷贝:
var obj = {
name: "abc",
age: 123,
sex: "female",
card: ["visa", "unionpay"],
};
var obj1 = {};
function clone(origin, target) {
var target = target || {};
for (var prop in origin) {
target[prop] = origin[prop];
}
return target;
}
clone(obj, obj1);
2.深层克隆
两个人,克隆,只考虑引用值的数组、对象 核心:区分数组和对象
var obj = {
name : 'abc',
age : 123,
sex : 'female',
card : ['visa','unionpay',[1,2]]
}
var obj1 = {
card : [obj.card[0],obj.card[1],[]]
}
clone(obj, obj1);
var obj = {
name : 'abc',
age : 123,
card : ['visa', 'master'],
wife : {
name : "bcd",
son : {
name : "aaa"
}
}
}
var obj1 = {
name : obj.name,
age : 123,
card : []//重新开始循环,发现里面都是原始值,card : [obj.card[0],obj.card[1]],
wife : {
name : "bcd",
son : {
name : "bcd",
son :
}
}//里面是不是原始值,第一个是,第二个不是, 建立新的对象,进行循环
}
遍历对象 for(var prop in obj)
遍历除了可以遍历对象,还可以遍历数组
var arr = ['a','b','c']
for(var prop in arr) {
arr[prop]
}
步骤:
1.判断是不是原始值 typeof()如果是 object 引用值,不是 obj,基本是原始值 null 最后讲 2.数组还是对象 三种方法: instanceof toString constructor,建议使用 toString,因为
另外两个有小问题,不会遇到
父子域:一个页面里面可能还有个子页面 跨父子域 [] instanceof Array ——>false 应该 ture 3.建立相应的数组和对象
// 递归
function deepClone(origin,target) {
var target = target || {};//容错
toStr = Object.prototype.toString,
arrStr = "[object Array]";//比对
}
代码实现
var obj = {
name : 'abc',
age : 123,
card : ['visa', 'master'],
wife : {
name : "bcd",
son : {
name : "aaa"
}
}
}
var obj1 = {
}
function deepClone(origin,target) {
var target = target || {};//容错
toStr = Object.prototype.toString,
arrStr = "[object Array]";
for(var prop in origin) {
if(origin.hasOwnProperty(prop)) {//防止原型链上的
if(typeof(origin[prop]) == 'object') {
if(toStr.call(origin[prop]) == arrStr) {
target[prop] = [];
}else{
target[prop] = {};
}
deepClone(origin[prop],target[prop]);//递归
}else{
target[prop] = origin[prop];
}
}
}
return target;
}
优化
function print(){
function deepClone(origin, target){
var target = target || {},
toStr = Object.prototype.toString,
arrStr = "[object Array]";
for(var prop in origin) {
if(origin.hasOwnProperty(prop)) {//null问题
if(origin[prop] !== "null" && typeof(origin[prop]) == 'object') {
//绝对不等于,隐式类型转换也不行
if(toStr.call(origin[prop]) == arrStr) {
target[prop] == [];
}else{
target[prop] = {};
}
deepClone(origin[prop],target[prop]);
}else{
target[prop] = origin[prop];
}
}
}
return target;
}
三目运算符简化代码
function print(){
function deepClone(origin, target){
var target = target || {},
toStr = Object.prototype.toString,
arrStr = "[object Array]";
for(var prop in origin) {
if(origin.hasOwnProperty(prop)) {//null问题
if(origin[prop] !== "null" && typeof(origin[prop]) == 'object') {
//绝对不等于,隐式类型转换也不行
target[prop] = (toStr.call(origin[prop]) == arrStr) ? [] : {};
deepClone(origin[prop],target[prop]);
}else{
target[prop] = origin[prop];
}
}
}
return target;
}
数组
数组的定义
区分对象的定义方式
(1) 自面量
(2) 构造函数——系统自带
(3) 自定义构造函数
(4) Var Obect.create()
数组 1.数组字面量 var arr = [];
var arr = [1,2,3,,,5];//稀松数组
2.new Array(length/content);
构造方法 var arr = new Array(1,2,3,4,5);
区别
var arr = new Array(10);//返回长度为10的稀松数组
var arr1 = [10];
var arr = new Array(10.2);//长度为10.2 报错
数组的读和写
arr[num] //不可以溢出读 结果undefined
arr[num] = xxx;//可以溢出写,撑长数组
JS 中,数组就算没有第 10 位也不报错,因为数组是基于对象的
数组常用的方法
1.改变原数组 7 个
reverse,sort,push,pop,shift,unshift,
push 把数组的最后一位增加
封装 push()
var arr = [1,2,3];
Array.prototype.push = function (){//不能写形参,因为不知道有几个,所以只能用arguments
for(var i = 0; i < arguments.length; i++) {
this[this.length] = arguments[i];
}
return this.length;
}
pop 删除:把数组的最后一位剪切出去
arr.pop(); 不能传参
shift:把前面减 arr.shift()
unshift:和 push 方向相反,在前面加东西
封装 unshift
数组不能向前面放东西,所以用新数组,在拼接起来(concat)
reverse:逆反
sort
arr.sort();//升序排序
arr.sort().reverse()//降序
但是这个排序是按 ASCII 排的,so
var arr = [1,2,10,2,4,5];不能实现想要的排序
实现排序:(冒泡排序) 1.必须两个形参 2.看返回值
(1)返回值为负数,前面的数放在前面
(2)为正数,后面的数在前
(3)为 0,不动
var arr = [1,2,10,2,4,5];
arr.sort(function (a, b) {
if(a > b) {
return 1;
}else{
return -1;
}
});
简化代码
arr.sort(function (a, b) {
if(a - b > 0) {
return a - b;
}else{
return a - b;
}
});
最终
arr.sort(function (a, b) {
return a - b;//升序
//return b - a;//降序
});
给一个有序数组乱序
Math.random() 返回(0,1)随机数
// var arr = [1,2,3,4,5,6,7];
// arr.sort(function () {
// return Math.random() - 0.5;//实现可正可负
// });
对象年龄排序
var cheng = {
name : "cheng",
age : 18,
sex : 'male',
face : "handsome"
}
var deng = {
name : "deng",
age : 40,
sex : undefined,
face : "amazing"
}
var zhang = {
name = "zhang",
age = 20,
sex = "male"
}
var arr = [cheng, deng, zhang];
arr.sort(function (a, b) {
// if(a.age > b.age) {
// return 1;
// }else{
// return -1;
// }
return a.age - b.age;
}
字符串长度排序
var arr = ['ac','bcd','cccc','asfsadshilk','casuicbniasbnciuas'];
arr.sort(function (a, b){
return a.length - b.length;
})
字节长度排序
function retBytes(str) {
var num = str.length;
for(var i = 0; i < str.length; i++){
if(str.charCodeAt(i) > 255){
num ++;
}
}
return num;
}
var arr = ['a邓','ba邓','cc邓cc','老邓',"残邓",'asdoifqwoeiur','asdf'];
arr.sort(function (a, b){
return retBytes(a)-retBytes(b);
})
splice:切片
// arr.splice(从第几位开始,剪切多少长度,在切口处添加新的数据)
arr.splice(1,2);
demo
var arr = [1,1,2,2,3,3];
arr.splice(1,1,0,0,0);
var arr = [1,2,3,5];//实现把4填进去
arr.splice(3,0,4);//鼠标光标在前面
arr.splice(-1,1);//倒数第一位
-1 倒数第一位;1 倒数 第二位怎么实现的
splice = function (pos) {
pos += pos > 0 ? 0 : this.length; //负数
};
2.不改变原数组
concat,join—>split,toString
- concat 连接
var arr = [1,2,3,4,5,6];
var arr1 = [7,8,9];
arr.concat(arr1);
toString
把数组变成字符串
Slice 截取
var arr = [1,2,3,4,5,6];
// 1.两个参数,slice(从该位开始截取,截取到该位)
// var newArr = arr.slice(1,3);
// 2.一个参数slice(从第几位开始截取,截取到最后)
var newArr = arr.slice(1)
var newArr = arr.slice(-4)//-4+6位
// 3.没参数:全截取
join
实现字符串连接
var arr = [1,2,3,4];
arr.join("-")//必须是字符串形式
arr = [1-2-3-4];
split()互逆方法:按照什么拆分
var arr = [1-2-3-4];
arr.split("3")//必须是字符串形式
类数组
是对象,可以当数组一样用
类数组 1.可以利用属性名模拟数组的特性 2.可以动态的增长 length 属性 3.如果强行让类数组调用 push 方法,则会根据 length 属性值的位置进行属性的扩充。
function test() {
console.log(arguments);
arguments.push(7);//报错
}
test(1,2,3,4,5,6);
现象
//完成类数组的基本形态构建
var obj = {
"0" : 'a',
"1" : 'b',
"2" : 'c',
"length" : 3,
"push" : Array.prototype.push
}
obj.push('d');
//导致length=4并且"3":d
类数组:是对象,可以当数组一样用
var obj = {
0: "a",
1: "b",
2: "c",
length: 3,
push: Array.prototype.push,
splice: Array.prototype.splice, //加上他就变成可以当数组用了,像数组了
};
//类数组组成部分:属性要为索引(数字)属性,必须有length属性,最好加上push
length 内部操作手法:
Array.prototype.push = function(target) {
this[this.length] = target;
this.length ++;
}
//如果对象调用,则
Array.prototype.push = function(target) {
obj[obj.length] = target;
obj.length ++;
}
类数组所有元素遍历出来
var obj = {
"0" : "a",
"1" : "b",
"2" : "c",
name : "abc",
age : 123,
length : 3,
push : Array.prototype.push,
splice : Array.prototype.splice
}
for(var prop in obj){
console.log(obj[prop])
}
封装 type
typeof([])– array
typeof({})– object
typeof(function)– function
typeof(new Number())– new Object
typeof(123)– number
分两类:1、原始值 引用值 2、区分引用值
function type(target) {
var template = {
"[object Array]" : "array",
"[object Object]" : "object",
"[object Number]" : "number - object",
"[object Boolean]" : 'boolean - object',
"[object String]" : 'string - object'
}
if(target === null){
return "null";
}
// if(typeof(target) == 'function') {
// return 'function';
// }else if(typeof(target) == "object") = {
if(typeof(target) == "object"){
// 数组;
// 对象;
// 包装类Object.prototype.toString
var str = Object.prototype.toString.call(target);
return template[str];
}else{//原始值
return typeof(target);
}
数组去重,在原型链上编程 hash 哈西
var arr = [1, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 1, 0, 0];
Array.prototype.unique = function () {
var temp = {},
arr = [],
len = this.length; //也是优化,不用每次都this了
for (var i = 0; i < len; i++) {
if (!temp[this[i]]) {
//如果有0,!0==true,所以还是"abc"吧
temp[this[i]] = "abc";
arr.push(this[i]);
}
}
return arr;
};
三目运算符
形式:判断语句? 若为真,执行,并返回结果:若为假,执行,并返回结果
三目运算符其实就是简化版的 if(){…}else{}语句
条件判断?是 :否 并且会返回值
var num = 1 > 0 ? 2 + 2 : 1 + 1;
var num = 1 > 0 ? ("10" > "9" ? 1 : 0) : 2;
try…catch
try{}catch(e) {}一行代码报错影响后面代码执行
finally{}
//try...catch
try {
console.log("a");
console.log(b);
console.log("c");
} catch (e) {}
console.log("d");
//执行到b就不执行了,在try里面发生的错误,不会执行错误后的try里面的代码,打印ad
关于 catch
try{
console.log('a');
console.log(b);
console.log('c');
}catch(e){//error error.message error.name ---> error
console.log(e.name + " : " + e.message);
}
console.log('d');
小问题
try{
console.log('a');
console.log(b);
console.log(c);//虽然错误,但是不执行
}catch(e){
console.log(e.name + " : " + e.message);
}
console.log('d');
Error.name 的六种值对应的信息:
1.EvalError:eval()的使用与定义不一致
2.RangeError:数值越界
3.ReferenceError:非法或不能识别的引用数值
4.SyntaxError:发生语法解析错误
5.TypeError:操作数类型错误
6.URIError:URI 处理函数使用不当
es5 严格模式
如今:基于 es3.0+es5.0 的新增方法 使用的,如果产生冲突,则遵循 es3.0 的
要讲的是怎么让他遵循 es5.0 解决,即 es5.0 的严格模式,则产生冲突的部分用 es5.0,否则 es3.0
“use strict”
不再兼容 es3 的一些不规则语法。使用全新的 es5 规范。
demo1:es5 不允许使用 arguments.callee
// es5.0严格模式的启动
"use strict"//放在逻辑的最顶端
function test() {
console.log(arguments.callee);
}
test();
demo2
function demo() {
console.log(arguments.callee);
}
demo(); //es3.0
function test() {
"use strict"; //内部es5.0
console.log(arguments.callee);
}
test();
两种用法:
全局严格模式
局部函数内严格模式(推荐)
就是一行字符串,不会对不兼容严格模式的浏览器产生影响
为什么用字符串”use strict”:可能在老浏览器执行,浏览器版本问题——向后兼容
es5 不允许用 with(){} with 可以改变作用域链,with(obj) obj 作为最顶端的 AO
var obj = {
name : "obj"
}
var name = 'window';
function test() {
var name = 'scope';
with(obj) {//如果with里面添加了对象,with(obj),会把对象当做with要执行的代码体的作用域链的最顶端
console.log(name);
}
}
test();
演示
var obj = {
name:"obj",
age:234
}
var name = "window";
function test(){
var age = 123;
var name = "scope";
with(obj){
console.log(name);//obj
console.log(age);//234
}
}
test();
with 作用:简化代码
var org = {
dp1 : {
jc : {
name : 'abc',
age : 123
},
deng : {
name : "xiaodneg",
age : 234
}
},
dp2 : {
}
}
with(org.dp1.jc) {//直接访问org.dp1.jc里面的
console.log(name);
}
应用:document 也是对象
document{
write : function () {}
}
with(document) {
write('a');
}
不支持 with,arguments.callee,func.caller,变量赋值前必须声明,局部 this 必须被赋值,(Person.call(null/undefined) 赋值什么就是什么),拒绝重复属性和参数
"use strict"
function test(){
console.log(this);//undefined
}
test();
new 了
"use strict"
function Test(){
console.log(this);//控制台显示constructor的名
}
new Test();
预编译在 es5 严格模式下 this 不在指向 window,没有指向(空),this 必须被赋值,赋值什么就是什么
"use strict"
function Test(){
console.log(this);
}
Test.call({});
赋值 123
"use strict"
function Test(){
console.log(this);
}
Test.call(123);
123 是原始值:ES3 就会变成包装类
function Test(){
console.log(this);
}
Test.call(123);
严格模式,在全局,this 指向 window
"use strict"
console.log(this);
es5 拒绝重复属性和参数。。es3 里面重复属性和参数是不报错的
function test (name , name){
console.log(name);
}
//test(1,2);
//test(2);
参数报错
"use strict"
function test (name , name){
console.log(name);
}
test(1,2);
属性不报错
"use strict"
var obj={
name:'112',
name:'111'
}
test(1,2);
关于 eval:
es3 不能用 eval();——能改变作用域
eval 能执行字符串
"use strict";
var a = 123;
eval('console.log(a)');//字符串在eval里面执行
面试题:为什么不用 with();
with 可以改变作用域链,改变作用域链,都会降低效率