JavaScript语法基础之一 (题目偏多)

新增常识/要求

  1. 书写格式,符号前后必须空格;块级注释 每行前面要有星号;每个逗号后面给个空格
  2. 方法名:第一个单词小写,后头的首字母大写

!= 不相等
!== 不全等

学习前的硬知识

▲5大主流浏览器 内核

  • ie —— trident (三叉戟
  • chrome —— webkit blink
  • safari —— webkit
  • firefox —— gecko (壁虎
  • opera —— presto (急板的 快速的

渐进式 web app

ECMA——欧洲计算机制造联合会
(European computer manufactures association )
ECMA - 262 脚本语言的规范

ES5 ES6:规范化脚本语言

解释型语言和编译型语言

在这里插入图片描述


day1

目标

  • 掌握变量的声明和使用方式
  • 理解数据类型的概念
  • 掌握不同的数据类型数据的书写方式和分别
  • 掌握不同数据类型之间相互转化的方式
  • 能够知道解释性语言和编译型语言的特点
  • 能够知道标识符不能是关键字或保留字

1.1 JS三部分组成
  • ECMAScript——JavaScript语法
    语法、变量、关键字 保留字 值
    原始类型,引用类型运算 对象 继承 函数

  • DOM document object model W3C (操作HTML文档

  • BOM browser object model (滚动条 窗口宽高 事件 事件冒泡、捕获 键盘、鼠标事件 正则

1.2 变量

1. 同时声明多个变量——逗号隔开

2. 声明变量特殊情况

情况说明结果
var age ; console.log (age);只声明 不赋值undefined
console.log(age)不声明 不赋值 直接使用报错
age = 10; console.log (age);不声明 只赋值10

3. 交换两个变量

  1. 使用一个 临时变量 用来做中间存储
  2. 加过来 再减去的方法 (笔试)
var a = 2;
var b = 4;

a = a + b; //6
b = a - b; //6-4=2
a = a - b; // 6-2=4
1.3 数据类型
1.3.1 简单数据类型(基本数据类型)

原始值—— 基本类型 (存在栈内存)

  • number
  • string
  • boolean
  • undefined
  • null (空值 初始化组件 函数 销毁函数 占位
1.数字型
  1. JS中八进制前面加,十六进制前面加0x

  2. 数字型三个特殊值
    ① infinity 无穷大
    ② - infinity 无穷小
    ③ NaN 非数

  3. isNaN( )
    用来判断一个变量是否为非数字的类型,返回 true 或 false

Number转换过来的值是不是NaN
Number(值) --> 与NaN进行比较 --> bool ;

2.字符串型
  1. 字符串引号嵌套
    引号嵌套双引号 ,或者用双引号嵌套单引号 (外双内单,外单内双)
  2. 字符串拼接
    字符串 + 任何类型 = 拼接之后的新字符串
    + 号总结口诀:数值相加 ,字符相连
1.3.2 复杂数据类型——引用值 (存在堆内存
  • object
  • array
  • function
  • date
  • RegExp 正则
内存之栈内存与堆内存

和Java那个应该是一样的,要去翻一翻Java那个

1.4 获取变量数据类型

typeof( )

注意:

  1. 任何typeof再typeof的都是string
  2. 未被定义的变量,如果你console.log的话会报错,但是有一种情况,你把它放到typeof里头是不会报错的,typeof会报undefined (而typeof的输出类型是字符串)
1.5 数据类型转换
1.5.1 转换为数字型(重点)
方式说明案例
parseInt(string)将string类型转成整数数值型parseInt(‘78’)
parseFloat(string)将string类型转成浮点数值型parseInt(‘78.43’)
Number() 强制转换函数将string类型转成数值型parseInt(‘12’)
js隐式转换 (- * /)利用算术运算 隐式转换为数值型parseInt(‘78’)

显示类型转换 Number( )

        var a = '123';
        console.log(Number(a) +"--"+typeof(Number(a))); // 123--number

        var b = 'abc';
        console.log(Number(b) +"--"+typeof(Number(b))); // NaN--number

        var c = '123abc';
        console.log(Number(c) +"--"+typeof(Number(c))); // NaN--number

        var d = true;
        console.log(Number(d) +"--"+typeof(Number(d))); // 1--number

        var e = false;
        console.log(Number(e) +"--"+typeof(Number(e))); // 0--number

        var f = null;
        console.log(Number(f) +"--"+typeof(Number(f))); // 0--number

        var g = undefined;
        console.log(Number(g) +"--"+typeof(Number(g))); // NaN--number

显示类型转换 parseInt( )

        var d = true;
        console.log(parseInt(d) +"--"+typeof(parseInt(d))); // NaN--number

        var e = false;
        console.log(parseInt(e) +"--"+typeof(parseInt(e))); // NaN--number

        var f = null;
        console.log(parseInt(f) +"--"+typeof(parseInt(f))); // NaN--number

        var g = undefined;
        console.log(parseInt(g) +"--"+typeof(parseInt(g))); // NaN--number

        var h = NaN;
        console.log(parseInt(h) +"--"+typeof(parseInt(h))); //NaN--number

console.log(parseInt('abc123'); // NaN
console.log(parseInt('123abc'); // 123
  • 它不管那么多,他只想转换成整型,所以他一定是跟数字相关的,他不会去把布尔类型转换成1
  • parseInt从第一位开始看只读取数字,如果第一位不是数字就判定为NaN;小数会被丢掉

▲ parseInt(a,radix)两个参数的方法。 radix基底——进制数
` 基底范围 2~36
当 基底为0时 相当于求整数,一个参数的
parseInt(10,2); 2进制的10 转换成 10进制对应的数是多少?2
parseInt(11,16); 16进制的11 转换成 10进制对应的数是多少? 17

显示类型转换 parseFloat( )

var num = parseFloat('3.1465925');
console.log(num.toFixed(2)); // 四舍五入 3.15

toFixed( ) 保留几位小数

隐式类型转换

  	  var a = '123';
      a++;
      console.log(a);// 124

      var b = '3' * 2;
      console.log(b); // 6

      var c = '1' > 2; //number
      console.log(c); // false

      var d = 1 > '2'; //number
      console.log(d);  // false

      var f = 1 == '1'; //number
      console.log(f);  // true

	  var g = 1 != '2'; //number
      console.log(h);  // true
      
      var e = 'd' > 'g'; //ascii
      console.log(e);

      var j = 2 > 1 > 3; // 1>3  按顺序从左到右比较
      console.log(j);  // false

      var k = 2 > 1 == 1; // 1==1
      console.log(k);  // true

	  var k = 2 > 1 == 1; // 1==1
      console.log(k);  // true

var a1 = undefined < 0;
console.log(a1);  // false

var a2 = undefined > 0;
console.log(a2);  // false

var a3 = undefined == 0;
console.log(a3);  // false

		var a1 = null< 0;
		console.log(a1);  // false
		
		var a2 = null> 0;
		console.log(a2);  // false
		
		var a3 = null == 0;
		console.log(a3);  // false
		
var a3 = null == undefined;
console.log(a3);  // true 

var a4 = NaN == NaN;
console.log(a3);  // false
  • < > >= <= 都要经过 number转换的
  • undefined 和 null 既不大于0也不小于0 也不等于0 他们和0没有关系
  • 但是! undefined和null互相有关系 【考过】
1.5.2 转换为字符串
方式说明案例
toString( )转成字符串
String( ) 强制转换转成字符串
加号拼接字符串和字符串拼接的结果都是字符串

numObj.toString([radix]); 也是有基底的
指定要用于数字到字符串的转换的基数(从2到36)。如果未指定 radix 参数,则默认值为 10。
·
【补充】后头有讲到
Object.prototype.toString()
每个对象都有一个 toString() 方法,当该对象被表示为一个文本值时,或者一个对象以预期的字符串方式引用时自动调用。默认情况下,toString() 方法被每个 Object 对象继承。如果此方法在自定义对象中未被覆盖,toString() 返回 “[object type]”,其中 type 是对象的类型。

1.5.3 转换为布尔型
方式说明
Boolean( )其他类型转成布尔值
  • 代表 空、否定的值 会被转换为false (‘ ’,0,null,NaN,undefined)
  • 其余值都会被转换为true

错误

语法 通用

不同的代码块 错误是不相影响的

console.log(0/0)  //NaN
console.log(1/0)  //infinity

++,- -问题

var a = 5,
	b;
b = a-- + --a;
console.log(b,a); // 8 3

把会先执行的放到前面来,后执行的放到后头。
b = - -a + a- - ; 4 + 4
a再减1 a=3

var a = 5,
	b;
b = --a + --a; 
console.log(b,a); // 7 3
var a = 5,
	b;
b = --a + a++;  // 4 + 4
console.log(b,a); // 8 5

比较运算符

  • 字符串比较大小
    按照字符串相对应的ASCII码,多个字符则从左到右一次对比直到比较出ASCII码的大小为止
  • NaN 与 包括自己在内任何东西都不相等

条件表达式

  • if-else (范围
  • switch-case (定值

注意:

  1. 互斥性
  • 如果多个条件表达式之间是 “互斥”关系,哪个判断和执行语句声明在上面还是下面,无所谓
  • 如果多个条件表达式之间有交集的关系,需要根据实际情况,考虑清楚哪个结构声明在上面
  • 如果多个条件表达式之间有包含的关系,通常情况下,需要将范围小的声明在范围大的上面。否则,范围小的就没计划执行

逻辑运算

undefined,null,NaN,“ ”,0,false都是假(其他都是真)

var a = 1 && 2; 
var b = 1 || 2;
console.log(a,b); // 2 1
var c = 0 || null || 1 || 0; //1
v

与:
遇到真 就往后走,
遇到假或走到最后就返回当前的值
或:
遇到假 就往后走,
遇到真或者走到最后就返回当前的值

for循环

var i = 0;
for(; i < 10;){
	console.log(i);
	i++;
}

while(i<10){
	console.log(i);
	i++;
}

for循环不是说固定就要3个式子那么写的。可以提出来写哦!
上面这样写 相当于while循环了

var i = 0;

for(; i;){
	console.log(i);
	i++;

	if(i == 11){
		break; // i = 0;
	}
}

打印0-100的数
( )里 只能有一句,不能写比较
{ }里 不能出现i++ i- -

var i = 100;
for(;i--;){
	console.log(i);
}

作业:

  • 用for循环,算出斐波那契数列的第N位
  • 背:number parseInt String toString 隐式类型转换 typeof全部背下来

        var n = parseInt(window.prompt('算出第几位?'));

        var a = 1,
            b = 0,
            c;


        for(var i=2;i<=n;i++){
            c = a + b;
            b = a;
            a = c;

            console.log('i='+i+':'+'a=' + a + '----' + 'b=' +b + '----' + 'c=' +c);
        }

知新
  • Number()强转,会把你转成数字型但不一定就能转成数字。只转纯数字字符串、bool、null

day2

目标

  • 掌握函数中arguments的使用方式
  • 掌握函数的两种声明方式
  • 理解函数的作用域概念
  • 掌握全局变量和局部变量的使用方式
  • 掌握作用域链的概念
  • 掌握预编译的工作原理
  • 掌握对象的创建方式
  • 掌握对象的属性访问方式

注意:

function test(){
	var a = b = 1; // 这个不是 在方法里声明 a b然后赋值为1;而是先声明了b=1,在方法里声明a然后将b赋值给a!
	console.log(a,b); // 1 1
}

test();
console.log(a) // 报错
console.log(b) // 1

  • 在实参内部传了值的,可以在函数中更改实参的值
  • 在实参里边并没有传值,你在函数中给这个形参赋值是没有用的
function test(a,b){
	b=3;
	console.log(arguments[1]); //undefined
}
test(1);

函数的两种声明方式

1. 命名函数(自定义函数方式)

function fn( ) { …}

2. 匿名函数(函数表达式方式)

var fn = funtion( ) { …}

  • 函数表达式方式原理与声明变量方式是一致的
  • 函数调用的代码必须写到函数体后面 (结合预编译)

arguments的使用

当我们不确定有多少个参数传递的时候,可以用arguments来获取。arguments对象中储存了传递的所有实参

arguments展示形式是一个伪数组,具有以下特点:

  1. 有length属性
  2. 按索引方式储存数据
  3. 不具有数组的 push,pop等方法

作业:

  1. 定义一个函数,从wp接收一个饮料的名称,函数返回对应的价格
  2. 定义一个函数,从wp接收第一个数,接收一个运算符号(+ - * / %),接收第二个数,利用这个函数做运算,并返回运算结果
  3. 定义一个函数,从wp接受一个n,算出n的阶乘,不能用for循环
  4. 定义一个函数,从wp接收一个n,算出斐波那契数列的第n位,不能用for循环

day3

  1. 参数初始化 形参的默认值:undefined
  2. arguments[ ] 实参
  3. 谁不是 undefined 我就选谁,要是都是undefined了 那就没办法只能是undefined了
  4. typeof打印出来的都是字符串
  5. 递归:
    ① 找规律
    ② 出口 (你要跳出来
    (队列)等着你结果出来了 才有return出去
预编译——变量、函数提升 ▲

GO、 AO

变量提升: 变量的声明会被提升到当前作用域的最上面,变量的赋值不会提升

函数提升: 函数的声明会被提升到当前作用域的最上面,但是不会调用函数

函数声明整体提升,变量只有声明提升,赋值是不提升

  • var a = 1; 是两个步骤

暗示全局变量

函数预编译:在函数要执行之前进行的一个步骤 AO (Action Object 活跃对象/函数上下文)

首先创建AO对象
然后:

  1. 寻找函数的 形参变量声明 (重复的写一次就可以了,只看你有没有变量声明,有就会放进AO 别的不管都还没执行呗)
  2. 把 实参值 赋值给 形参
  3. 寻找函数声明并赋值
  4. 执行函数

题:

        function test(a,b){
            console.log(a); //1
            c = 0;
            var c;
            a = 5;
            b = 6;
            console.log(b); 6
            function b(){}
            function d(){}
            console.log(b); 6
        }

        test(1)
        //AO = {
        //     a:undefined -->1,
        //     b:undefined -->function b(){},
        //     c:undefined,
        //     d:function d(){}
        // }

全局上下文 GO: 在通篇 js执行之前产生了GO 全局上下文 GO === window

  1. 寻找变量声明
  2. 寻找函数声明
  3. 执行

题1:

        console.log(a,b); // a(),undefined
        function a(){}
        var b = function(){}

        //GO{
        //     b:undefined,
        //     a:function a(){}
        // }

题2:

        function test(){
            var a = b = 1;
            console.log(b);
        }

        test();
        //GO{
            // b :1
        // }
        //
        //
        //看到有test(),所以有AO
        //AO{
        //     a:undefined -->1
        // }

自己没有的会到GO里头去找看看有没有 +1

题3:

		var b = 1;
        function test(){
            var a = 1;
            var b = 2;
            console.log(b); //2
        }

        test();

函数内部有的话,肯定是找自己的

题4:

        var b = 3;
        console.log(a); 								//function a(a)
        function a(a){
            console.log(a); 							//function a()
            var a = 2;
            console.log(a); 							// 2 
            function a(){}
            var b = 5;
            console.log(b); 							// 5
        }

        a(1);

        //GO{
        //     b:undefined
        //     a:function a()
        // }

        //AO{
        //     a:undefined -->1 --> function a() -->2,
        //     b:undefined -->5, //执行的时候会赋值的
        // }

执行的时候是会赋值的!

题4:

        a = 1;
        function test(){
            console.log(a); 						//undefined
            a = 2;
            console.log(a); 						// 2
            var a = 3;
            console.log(a); 						// 3
        }

        test();
        var a;

        //GO = {
        //     a:undefine ,
        //       1
        //     test:function test(){}
        // }

        //AO = {
        //     a:undefined,
        //       2,
        //       3,
        // }

AO里头有a的话,不管是什么值,都不会到GO里头找

题5:

        function test(){
            console.log(b); 							//undefined
            if(a){
                var b = 2;
            }

            c = 3;
            console.log(c); 							// 3
        }

        var a;
        test();
        a = 1;
        console.log(a); 								//1

        //GO = {
        //     a:undefined --,
        //     test:function test(){}
        //     c:3
        // }

        //AO = {
        //     b:undefined,
        // }

只看你有没有变量声明,有就会放进AO

作业:

		//------------------ 1
        function test(){
            return a;
            a = 1;
            function a(){}
            var a = 2;
        }

        console.log(test());  								// function a(){}
		// AO{
        //     a:undefined --> function a(){},
        // }


        //------------------ 2

        function test(){
            a = 1;
            function a(){}
            var a = 2; 
            return a; //2
        }

        console.log(test());

        //AO{
        //     a:undefined --> function a(){} -->1 -->2,
        // }


        //---------------- 3
        a = 1;
        function test(e){
            function  e(){}
            arguments [0] = 2;
            console.log(e); //2
            if(a){
                var b = 3;
            }
            var c;
            a = 4;
            var a;
            console.log(b); //undefined
            f = 5;
            console.log(c); // undefined
            console.log(a); //4
        }

        var a;
        test(1)
		console.log(a);
		console.log(f);

        // GO{
        //     a:undefined,
        //     test:function test(),
        //     f:5
        // }

        //AO{
        //     e:undefined --> 1 (实参赋值) --> function e() (函数声明赋值) --> 2 (执行时赋值),
        //     b:undefined,
        //     c:undefined,
        //     a:undefined --> 4 (执行赋值),
        // }

day2 复习:

var a = false + 1;
console.log(a); 							// 1


var b = false == 1
console.log(b); 							// 0 false



//-------------------

if(1 + 5 * '3' ===16){
	console.log('通过了');
}else{
	console.log('未通过');
}
// 通过了

//-----------------------------------

console.log(!!' ' + !!'' - !!false || '通过了'); // 0+0-0 错 --> 第一个不是空字符串,所以:1+0-0 = 1
// 通过了 错--> 1

//--------------------------------

window.a || (window.a = '1'); // 当前没有a,所以window.a=undefined 所以为假 往后走
console.log(window.a) //1

//错误! 因为后头加了括号,括号的优先级最高,后头的要比前面的先执行,后头的执行完回到前面来执行 遇到真就停止了

//----------------------------

//-------------------感觉这题有点难 没搞清楚

if(typrof(a) && (-true) + (+undefined) + ''){ // 'undefined' && (-1 + NaN + ' ')
// -1 + NaN = NaN -1 = NaN 
// NaN + '' = 'NaN'
    console.log('ok')
}else{
    console.log('no')
}
// ok

未被定义的变量,如果你console.log的话会报错,但是有一种情况,你把它放到typeof里头是不会报错的,typeof会报undefined (而typeof的输出类型是字符串)

day4

作用域、作用域链、闭包、立即执行函数、表达式

1. 作用域 scope、作用域链

函数也是一种对象类型 引用类型 引用值
函数也是具有属性的

function test(a,b){
}
console.log(test.name); // test
console.log(test.length); // 2

作用域 [[ scope ]]:

  1. 函数创建时,生成的一个JS内部的隐式属性。(只有JS引擎才能读取)
  2. 存储作用域链的容器

作用域链:

  1. 用来存放 AO/GO
  2. 函数执行完成以后,AO是要销毁的。(AO是一个即时的存储容器)

每一个函数 他的作用域链上都有GO

  • 当函数被定义的时候,就生成了 [[ scope ]] -> scope chain ->GO

  • 当你这个函数在执行前的那一刻会生成AO

例子:

    function a(){
        function b(){
            function c(){

            }
            c();
        }
        b();
    }
    a();

	// a定义: a.[[scope]] -> 0 : GO
    // a执行: a.[[scope]] -> 0 : a的AO
    //                       1 : GO
    // b定义: b.[[scope]] -> 0 : a的AO
    //                       1 : GO
    // b执行: b.[[scope]] -> 0 : b的AO
    //                       1 : a的AO
    //                       2 : GO
    // c定义: c.[[scope]] -> 0 : b的AO
    //                       1 : a的AO
    //                       2 : GO
    // c执行: c.[[scope]] -> 0 : c的AO
    //                       1 : b的AO
    //                       2 : a的AO
    //                       3 : GO   
    // c执行结束: c.[[scope]] -> 0 : c的AO 删除X
    //                           1 : b的AO
    //                           2 : a的AO
    //                           3 : GO 
    //回到c定义的状态
    // c执行结束: c.[[scope]] -> 0 : b的AO
    //                           1 : a的AO
    //                           2 : GO
    // b执行结束: b.[[scope]] -> 0 : b的AO 删除X
    //                           1 : a的AO
    //                           2 : GO
    //            同时 c.[[scope]] X没有了
    // b执行结束: b.[[scope]] -> 0 : a的AO
    //                           1 : GO
    // a执行结束: a.[[scope]] -> 0 : a的AO 删除X
    //                           1 : GO
    //            同时 b.[[scope]] X没有了
    // a执行结束: a.[[scope]] -> 0 : GO

2. 闭包:

  1. 简单理解:一个作用域可以访问另外一个函数的局部变量

  2. 内部函数 被返回,外部并保存时,一定会产生闭包,闭包会使原来的作用域链不释放 (它被返回出去的时候 它死死的抓住上一级AO)(因为,内部函数被定义时,是在外部函数环境下,所以内部函数这时的作用域链有外部函数执行期的作用域链)

  3. 闭包可以延伸变量的作用范围、做一个数据缓存

  4. 过多的闭包可能会导致内存泄漏,或加载过慢。

// 闭包
function test1(){
    var n = 100;
    function add(){
        n++;
        console.log(n)
    }

    function reduce(){
        n--;
        console.log(n)
    }

    return [add,reduce];
}

var arr = test1();

arr[0](); // 101
arr[1](); // 100

//============

function sunSched(){
	var opration = {
		setSched: function(){
			sunSched = thing;
		},
		show: function(){
			console.log("my schedule on sunday is" + sunSched);
		}
	}

	return opration;
}
var sunSched = sunSched();

sunSched.setSched('studing');
sunSched.showSched();

  1. 想把两个函数返回出去 怎么返回?
    用数组/对象 对象怎么返啊!?

循环注册点击事件——闭包笔试常考题

在这里插入代码片

3. 立即执行函数 —— IIFE immediately-invoked function expression (初始化函数)

特点:

  1. 页面加载的时候它就自动执行了
  2. 执行完成以后立即释放

立即执行函数是有返回值的,你要接收它的返回值,就把它交给一个变量来保存

写法:

  1. (function( ){ })( );
  2. (function( ){ }( )); // W3C建议
  3. 在函数前面加 + - !|| &&

4. 表达式

  • 括号包起来的,不管里头是什么 都变成表达式了
  • 一定是表达式才能被执行符号执行
  • 表达式会自动忽略你的函数名

那怎么样的才是表达式?

① 常规字面量声明那种
② 被括号包起来了

(function test1(){
	console.log(1);
})(); //1

var test2 = function(){
	console.log(1);
}(); //1

function test3(){
	console.log(1);
}() // 这不是表达式,所以不能执行

函数声明变成表达式的方法: 在函数前面加 + - !|| &&
将函数声明转化为表达式后,就可以使用执行符号,立即执行该函数并且 该函数声明的函数名自动被忽略掉了

面试题:会报错吗?

function test(a,b){
	console.log(1);
}(6)

//> 不会,js引擎是这样解析的:
function test(a,b){
	console.log(1);
}
(6) //它认为你这个是表达式,而方法没有被调用就不会执行
//你啥都不传的话,它只能认为你是一个立即执行符号

(6) //它认为你这个是表达式,而方法没有被调用就不会执行
//你啥都不传的话,它只能认为你是一个立即执行符号

5.(应该是指在表达式中) 逗号(,) 是个运算符。它只返回所有逗号的最后一个值

面试题:

var fn =(
		function test1(){
			return 1;
		},
		function tese2(){
			return '2';
		}
	)();

// fn = (1,'2');
console.log(typeof(fn)); //string

5. 闭包&&立即执行函数经典案例:

funtion test(){
	var arr = [];

	for(var i = 0; i < 10; i++){
		arr[i] = function(){
			document.write(i + ' ');
		}
	}

	return arr;
}

var myArr = test();

for(var j = 0;j < 10;j++){
	myArr[j]();
}

现在打印出来的全是10 (与闭包有关)
那么怎么写才能打印出0-9

第一种:在你循环的时候就立即执行了

funtion test(){
	//var arr = [];

	for(var i = 0; i < 10; i++){
		 (function(){
			document.write(i + ' ');
		}())
	}
}

第二种:借助外界力量

funtion test(){
	var arr = [];

	for(var i = 0; i < 10; i++){
		arr[i] = function(num){
			document.write(num + ' ');
		}
	}

	return arr;
}

var myArr = test();

for(var j = 0;j < 10;j++){
	myArr[j](j);
}

第三种:还是用立即执行,每一次把这个值给保存下来(这是很实用的一种方法!必会 )

funtion test(){
	var arr = [];

	for(var i = 0; i < 10; i++){
		(function(j){
			arr[j] = function(){
			document.write(j + ' ');
			}
		})(i);
	}

	return arr;
}

var myArr = test();

for(var j = 0;j < 10;j++){
	myArr[j]();
}

有点难、有点绕

var a = 10;

if(function b(){}){ // 它不是false 它肯定进去执行 (function b(){})是表达式,所以函数名被忽略了
//函数在这一步的时候已经不存在,b 变成 is not defined 
//如果b没有被声明打印出来是会报错的,只有一种情况是放到typeof里面是不会报错的显示的是undefined
	a += typeof(b)
}

console.log(a);  //10function 错-->10undefined

b为什么是undefined

作业:

  1. 累加器 闭包 0 执行一次+1
  2. 一个班级,学生名字保留在一个数组里,两个方法写在函数中的一个对象中,第一个方法写加入班级,第二个方法离开班级每次加入或离开都需要打印新的学生名单
// 作业1
        function add(){
            var count = 0;
            function add1(){
               // console.log(count++); //这样写第一次调用的时候打印出来为0
               console.log(++count);
                
            }
            return add1;
        }

        var test = add();
        test();

day

目标

  1. 能够使用构造函数创建对象
  2. 能够说出原型的作用
  3. 能够说出访问成员的规则

对象

创建对象的方式:
  1. 对象字面量
  2. new Object
  3. 自定义函数
对象的增删改查

怎么删?

        var teacher = {
            name:'张三',
            age:32,
            sex:'male',
            height:176,
            weight:120,
            teach:function(){
                console.log('i am teaching');
            },
            smoke:function(){
                console.log('i am smoking');
            },
            eat:function(){
                console.log('i am eatching');
            }
        }

        //增
        teacher.address = '北京';

        //删
        delete teacher.address;        
        delete teacher.teach(); //能成功删除吗? 不能,后边有个执行 (笔试题)

        delete teacher.teach

        console.log(teacher);
构造函数
  • 系统自带的构造函数 与对象字面量相等
var obj = new Object();
obj.name = 'lili';
obj.sex = 'female';

new 在执行时会做四件事情:

  1. (隐式的)在内存中创建一个新的空对象
  2. 让 this 指向这个新的对象
  3. 执行构造函数里面的代码,给这个新对象添加属性和方法
  4. (隐式的)返回这个新对象
自定义构造函数
  • 大驼峰 所有单词首字母大写
  • 封装插件、模块化
function Teacher(){
	this.name = '张三';
	this.sex = '男';
	this.smoke = function(){
		console.log('i am smoking');
	}
}

这个this指代的是谁啊? 构造函数没有执行之前 这个this根本就不存在

你要让this存在必须要实例化它,谁用就是谁
this指向的是实例化的对象

var t = new Teacher(); // t = this

传参:传一个对象进去

function Teacher(opt){
	this.name = opt.name;
	this.sex = opt.sex;
	this.smoke = function(){
		console.log('i am smoking');
	}
}

解析this指向

作业:

  1. 写一个构造函数,接受数字类型的参数,参数数量不定,完成参数相加和相乘的功能

  2. 写一个构造车的函数,可设置车的品牌、颜色、排量
    再写一个构造消费者的函数,设置用户的名字,年龄,收入,通过选择的方法实例化该用户喜欢的车,再设置车的属性

// 作业1
        function Computate(opt){
            this.num = opt.num;
            this.add = function(){
                var sum = 0;
                for(var i = 0; i<this.num.length;i++){
                    sum += this.num[i];
                }
                console.log(sum);
            }
            this.aMultiply = function(){
                var accumulate = 1;
                for(var i=0; i<this.num.length;i++){
                    accumulate *= this.num[i];
                }
                console.log(accumulate);
            }
        }

        var compute = new Computate({
            num:[1,2,3,4]
        });
        compute.add();
        compute.aMultiply();

//作业2 --------------------------------------------
        function Cars(opt){
            this.brand = opt.brand;
            this.color = opt.color;
            this.displacement = opt.displacement;

            console.log('brand:' + this.brand);
            console.log('color:' + this.color);
            console.log('displacement:' + this.displacement);
        }
       function Customer(opt){
            this.name = opt.name;
            this.age = opt.age;
            this.income = opt.income;
            this.choose = function(){
               
                
                console.log('age:' + this.age);
                console.log('income:' + this.income);

               	var car = new Cars(opt.cars);

               	console.log( this.name + '购买了一辆' + car.color + '的' +  car.brand + '排量是' + car.displacement);
            }
        }

        var customer1 = new Customer({
            name:'lili',
            age:21,
            income:23425,
            cars:{
                brand:'奔驰',
                color:'pink',
                displacement:5
            }
        })
        customer1.choose();

此题参数问题:
在方法里实例化车的构造函数,只需要把参数传到构造函数里就可以了。不用放在choose方法的参数里,这样opt.cars就不会报错

优化for循环性能的一种方式:缓存一下

包装类

  • 原始值没有自己的 方法 和 属性
  • new Number( )
  • new String( )
  • new Boolean( )
var a = 1;
console.log(a);

// 数字不一定是原始值
var b = new Number(a);
b.len = 1;
b.add = function(){
	console.log(1);
}
console.log(b);

var d = b + 1;
console.log(d) // 2 参与运算时又会返回原始值

// 当数字经过 new Number 了以后,他就会变成数字对象
// 这样就可以设置属性和方法了

console.log(b);
  • 对象参与运算的时候会回到原始值

  • undefined 和 null 是不可以设置任何属性和方法的

数组的截断方法

var arr = [1,2,34,5,3];
arr.length = 3;
console.log(arr); // [1,2,34]

为什么string 就可以 .length? —> 通过包装类来访问(或者说字符对象有这个属性


var str = 'abc';

str.length = 1; // 原始值,new String(str).length = 1; (执行后发现没地方保存)
//delete 

// new String(str).length 经过包装类来访问
console.log(str.length); //3

题1:

var name = 'liuguiji';
name +=10; // 'liuguiji10'

var type = typeof(name); // 'string'

if(type.length === 6){
	type.text = 'string'; // type是原始值,那你是顾客你要求了所以没办法只能给你操作一遍
	// new String(type).text = 'string'; 执行完之后发现没有地方储存,所以自删除
}

console.log(type.text); //undefined

偏要你输出type.text 怎么办?加一个包装类

var type =new String(typeof(name));

题2:

function Car(brand,color){
	// this = {
	// }
	this.brand = 'Benz';
	this.color = 'red';
}

var car = new Car ('Mazda','blank'); 

console.log(car); //你没有把参数赋值,所以开始原来写好的值

题3 :

function Test(a,b,c){
	var d = 1;
	this.a = a;
	this.b = b;
	this.c = c;

	function f(){
		d++;
		console.log(d); //先d++再打印的d,所以加了1
	}

	this.g = f;
	//return this; --> 闭包
}

var test1 = new Test();

test1.g(); //2
test1.g(); //3

var test2 = new Test();
test2.g(); //2

题4:

var x = 1,
	y = z = 0;

function add(n){
	return n = n + 1;
} 

y = add(x); //2

function add(n){
	return n = n + 3;
}

z = add(x); //4

console.log(x,y,z); // 1 2 4

//预编译:
//GO = {
//	x:1,
//	y:0,
//	z:0,
//	add:function add(n){return n = n + 3} //覆盖了 所以最后执行的都是这一函数体
//}

//全局上下文 函数提升 变量声明提升
// 同样的名称放到同样的地方去覆盖

答案:144

题5:哪个函数可以输出 1 2 3 4 5? (关于立即执行函数

function foo1(x){
	console.log(arguments);
	return x;
}

foo1(1,2,3,4,5);

function foo2(x){
	console.log(arguments);
	return x;
}(1,2,3,4,5); //这个函数没有执行,被看成 函数+一个表达式
 	// function foo2(x){
	// 	console.log(arguments);
	// 	return x;
	// }
	// (1,2,3,4,5);

(function foo3(x){
	console.log(arguments);
	return x;
})(1,2,3,4,5);

foo1 和 foo3

arguments 存储传递的所有实参,不管你形参有几个,你实参传了几个都会被存储进去

题:

function b(x,y,a){
	a = 10;
	console.log(arguments[2]); //10

	//另一种
	arguments[2] = 10;
	console.log(a); //10 映射关系
}

b(1,2,3);

作业:

 //ASCII码 表1 0-127 表2 128-255 1个字节 byte
//
//UNICODE码 涵盖ASCII码  256开始2个字节
// 
var str = 'a';

var pos = str.charCodeAt(0);
console.log(pos);

写一个函数,接受任意一个字符串算出这个字符串的总字节数

//自己写的:
function byteSum(str){
	var item,
		sum = 0;
	for(var i=0;i<str.length;i++){
		item = str.charCodeAt(i);
		if(item > 255){
			sum +=2;
		}else{
			sum++;
		}
	}
	console.log(sum);
}

byteSum('abc');
byteSum('小i选哪个');
// 这种方法还是有些冗余

//老师优化版:
//中文占2个字节 只比字母数字多一个字节,所以就再判断多出来的字节
function byteSum(str){
	var sum = str.length,
		pos;
	for(var i=0;i<str.length;i++){
		pos = str.charCodeAt(i);
		if(pos > 255){
			sum++;
		}
	}
	console.log(sum);
}

byteSum('abc');
byteSum('小i选哪个');

讲评在原型与原型链深入、对像继承


day

原型对象 prototype

构造函数方法很好用,但是存在浪费内存的问题
构造函数通过原型分配的函数让所有对象实现共享操作(不用单独开辟内存空间)

  • 每一个构造函数里边都有一个原型对象(console.dir(构造函数名称) )可以查看到
  • 是function对象的一个属性
    打印出来看了一下,结果它(原型)也是对象,所以称为原型对象

  • 这个prototype是定义构造函数构造出的每个对象的公共祖先

  • 所有被该构造函数构造的对象都可以继承原型的属性和方法

  • (实例化对象)自己有的属性就不会到原型上去找属性了

  • 构造出来的对象对原型的增删改查问题——只能查

作用:
我们在实例化对象的时候总有一些写死的值
这些写死的值,在每次new的时候我们都需要去走一遍这个流程,这种代码对于实例化来说是一种代码的冗余,也算是一种耦合(重复了)在这种情况下,我们得想一个办法 能不能让它继承谁,在这种时候,我们把要写死的内容挂到原型上去
当我们需要用参数去传值的这些, 我们就写到this里面去,当我们需要写死的这些,我们就写到原型上去直接继承就可以了

经验:
一个插件,方法往往被写到原型上去。部分属性写到构造函数内部。因为属性往往都是配置项 需要传参去配置的,而方法都是一样的

function Handphone(brand,color){
	this.color = color;
	this.brand = brand;
	this.screen = '16:9';
}

// Handphone.prototype.rom = '64G';
// Handphone.prototype.ram = '6G';
// Handphone.prototype.screen = '18:9';
// Handphone.prototype.system = 'Android';
// Handphone.prototype.call = function(){
// 	console.log('i am calling');
// }
// 
// 优化代码:
Handphone.prototype = {
	rom:'64G',
	ram:'6G',
	screen:'18:9',
	system:'Android',
	call:function(){
		console.log('i am calling');
	},
}
var hp1 = new Handphone('小米','red');
var hp2 = new Handphone('华为','black');
console.log(hp1.rom);
console.log(hp2.ram);
console.log(hp1.screen); // 16:9 自己有的属性就不会到原型上去找属性了
console.log(hp2.screen); // 16:9 自己有的属性就不会到原型上去找属性了

hp2.call();

对象原型 __proto __

对象都会有一个属性__proto __ 指向构造函数的prototype 原型对象 (我们对象 可以使用构造函数prototype 原型对象的属性和方法,是因为对象有__proto __原型的存在)

  • _proto_是实例化以后的结果,属于对象实例的
    每个实例化对象原型的容器,它就是装prototype的
    proto也是能改的(更改装的内容)
function Car(){
	// new实例化以后,隐式的var了一个this的一个空对象
	// var this = {
	 	__proto__:Car.prototype 
	// }
	// 
}

Car.prototype.name = 'Benz';

var car = new Car();
console.log(car);

  • 原型上面的构造器默认是指向构造函数本身的
    可以通过更改原型里的构造器,硬给它指向别的

  • 如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,他会把原来的constructor覆盖掉,则必须手动的利用constructor指回原来的构造函数

  • constructor的作用:告诉我们你这个对象是通过哪个构造函数创建出来的

Handphone.prototype = {
	constructor:Telephone,
	}
var  p1 = {
	name:'里斯'
}
car.__proto__ = p1;
  • 原型一定是属于实例化对象的而不是构造函数的
function Car(){}
Car.prototype.name = 'Mazda';

var car = new Car();

Car.prototype.name = 'Benz';

console.log(car.name); //Benz


//-------------
Car.prototype.name = 'Mazda';
function Car(){}
var car = new Car();
Car.prototype.name = 'Benz';

console.log(car.name); //Benz

//--------------
Car.prototype.name = 'Mazda';
function Car(){}
Car.prototype.name = 'Benz';
var car = new Car();


console.log(car.name); //Benz

以上都是对属性的重写

Car.prototype.name = 'Benz';
function Car(){}
var car = new Car();

Car.prototype = {
	name:'Mazda'
}
console.log(car.name); //Benz
解析
// fucntion Car(){
// 	this{
//		__proto__:Car.prototype {
//			name:'Benz'
//		}
// 	}
// }
// 
// Car.prototype.cunstructor  -> Car() -> prototype -> name:'Mazda'

属于给prototype的name赋值,另一个属于重写prototype

window return

// 
function test(){
	var a = 1;
	function plus1(){
		a++;
		console.log(a);
	}
	return plus1; //把plus送到全局去了,变成了全局函数
}

var plus = test(); //用plus将它保存 需要一个全局变量去保存它
plus();
plus();
plus();



function test(){
	var a = 1;
	function add(){
		a++;
		console.log(a);
	}

	window.add = add;
}

test();

add(); //2
add(); //3
add(); //4


var add = (function(){
	var a = 1;
	function add(){
		a++;
		console.log(a);
	}
	return add; // 或 window.add = add;
})(); 

add(); //2
add(); //3
add(); //4

  • 自启动函数
  • 写立即执行函数,首先打一个分号 这是一种习惯 怕忘了打

js插件的写法:

立即执行函数里面写一个构造函数
将这个构造函数保存到window上的一个变量

  • 一个自启动函数,插件的标配

(function(){
	function Test(){}

	window.Test = Test;
})();

var test = new Test();

作业:写一个插件
任意传两个数字,调用插件内部方法可进行加减乘除功能

prototype中 怎么拿到参数、构造函数的属性?
答:
在实例化对象后,你调用方法,发现构造函数里头没有就会到原型里头去寻找。 谁使用this就指向谁

// ;(function(){
// 	function Compute(opt){
// 		this.a = opt.a;
// 		this.b = opt.b;

// 		this.add = function(){
// 			var sum = 0;
// 			sum = this.a + this.b;
// 			console.log(sum);
// 		};
// 		this.reduce = function(){
// 			var red = 0;
// 			red = this.a - this.b;
// 			console.log(red);
// 		};
// 		this.mul = function(){
// 			var acc = 1;
// 			acc = this.a * this.b;
// 			console.log(acc);
// 		};
// 		this.divide = function(){
// 			var shang;
// 			shang = this.a / this.b;
// 			console.log(shang);
// 		}
// 	}

// 	window.Compute = Compute;
// })();
// 
// 
;(function(){
	function Compute(opt){
		this.a = opt.a;
		this.b = opt.b;
	}

	Compute.prototype = {
		add:function(){
			var sum = 0;
			sum = this.a + this.b;
			console.log(sum);
		},
		reduce:function(){
			var red = 0;
			red = this.a - this.b;
			console.log(red);
		},
		mul:function(){
			var acc = 1;
			acc = this.a * this.b;
			console.log(acc);
		},
		divide:function(){
			var shang;
			shang = this.a / this.b;
			console.log(shang);
		}

	}

	window.Compute = Compute;
})();

var compute = new Compute({
	a:20,
	b:4
});

compute.add();
compute.reduce();
compute.mul();
compute.divide();

作业笔记:
参数可以在调用方法的时候传递,所以可不写在构造函数上,写在方法上

原型链

  • 原型链的顶端 Object.prototype
    object仍然是有原型的

  • Object.prototype底下保存了一个 toString的方法

  • 原型链上的增删改只能是它自己本身(不是绝对的)

原型深入

题1:

//面试题:

Professor.prototype.tSkill = 'JAVA';
function Professor(){}
var professor = new Professor();

Teacher.prototype = professor;
function Teacher(){
	this.mSkill = 'JS/JQ';
	this.success = {
		alibaba:'28',
		tencent:'30'
	}
}
var teacher = new Teacher();

Student.prototype = teacher;
function Student(){
	this.pSkill = 'HTML/CSS';
}

var student = new Student();
//这样更改他的父级元素(就是他老师的属性)行不行
//再验证一下这条语句会不会在student里面加一个新的success属性,然后这个属性对应一个对象,teacher里头的会不会被复制过来,且里头写上百度
student.success.baidu = '100';
console.log(teacher,student);

//这两个打印有没有变化?
//teacher的加上了
//他没有赋值到student实例里边,他赋值到实例的原型里边(这是因为你teacher变了,你把teacher赋值到Student.prototype,所以他在实例的对象原型里)

//学生可以修改teacher的引用值属性

题2:

//面试题:

Professor.prototype.tSkill = 'JAVA';
function Professor(){}
var professor = new Professor();

Teacher.prototype = professor;
function Teacher(){
	this.mSkill = 'JS/JQ';
	this.students = 500;
}
var teacher = new Teacher();

Student.prototype = teacher;
function Student(){
	this.pSkill = 'HTML/CSS';
}

var student = new Student();

student.students++;
console.log(teacher,student);
//这两个打印有没有变化?

//老师不变,学生的增加了一个students属性值为501
//学生不能去修改teacher的原始值属性,但是它会将teacher。students复制下来,他认为你想给student增加一个students属性
// student.students = student.students + 1 (student.students取值,因为往上找能找到为500 所以是500+1)

题3:

//笔试题
//
//
function Car(){
	this.name = 'Benz';
}

Car.prototype = {
	brand:'Mazda',
	intro:function(){
		console.log('我是' + this.brand + '车'); 
	}
}

var  car = new Car();

//new了:
//function Car(){
//	var this = {
//		brand:'Benz'
//	这个时候要调用intro方法,但是我对象里头没有。所以就到原型里面去找
//	}
//	Car.ptototype--> intro() 里的this指向对象本身,刚好我对象里头有brand 所以打印出Benz
//	
//}
car.intro(); //Benz

谁在使用this,this就指向谁

那么想让打印出来的是Mazda得怎么写?

Car.prototype.intro();

题3:

  • 普通函数不写返回值,默认返回undefined return undefined;
  • 构造函数通过实例化了以后,返回的是this
function Person(){
	this.smoke = function(){
		this.weight--;
	}
}

Person.prototype = {
	weight:130
}

var person = new Person();

person.somke();
// this.weight = this.weight - 1;
//               取值,原型里有
//               130 -1
// 而person里没这个属性
// person添加weight属性
console.log(person);

创建对象(4种):

用字面量和系统自带的构造器去声明的对象,他们的构造器都是Object,他们两的原型都是原型链顶端的 Object.prototype

var obj1 = {};
console.log(obj1);

var obj2 = new Object(); //公司一般不用这个,因为它和字面量声明没有区别,添加属性什么的又麻烦、乱
console.log(obj2);

function Obj(){}
var obj3 = new Obj();
console.log(obj3); //它的构造器指向的是Obj()自定义的构造器
  • 除了写插件,尽量都用字面量去声明对象

  • 原型的原型一定是Object构造出来的


构造函数

Object.create(对象/null) //必须得放内容,二选一

  • 功能:你可以指定对象原型,(里面放你想要的原型)
function Obj(){}
Obj.prototype.num = 1;
var obj1 = Object.create(Obj.prototype); 
var obj2 = new Obj();
console.log(obj1);
console.log(obj2); 

他们两个其实是一样的,对象原型、构造函数都是一样的
new的工作:

  • 实例化obj2
  • 调用构造函数Obj的初始化属性和方法
  • 指定实例对象的原型 proto:Obj.prototype
//创建obj1空对象
var obj1 = Object.create(null); //完全空的,对象原型都没了__proto__

是不是所有的对象都应该继承 Object.prototype 呢?
不是,Object.create(null)这种对象就不会继承


undefined null 能不能使用toString()?

不能,undefined和null不能经过包装类,且他们没有原型

var num = 1;
num.toString(); //1
这个可以是因为它经过了包装类

那为什么Number要有一个自己的toString(),而不继承Object.prototype里面的toString()
// 方法的重写,因为Object的不满足需求
var num = 1;
var obj = {};
var obj3 = Object.create(null);

document.write(num);
document.write(obj);
document.write(obj3); //报错

//document.write()打印的时候有一个隐式转换,转换成string

call、apply 更改this的指向

案例:

function Compute(){
	this.plus = function(a,b){
		console.log(a + b);
	}
	this.minus = function(a,b){
		console.log(a-b);
	}
}

function FullCompute(){
	Compute.apply(this);
	this.mul = function(){
		console.log(a * b);
	}
	this.div = function(){
		console.log(a / b);
	}
}

var compute = new FullCompute();

compute.plus(1,2);
compute.minus(1,2);
compute.mul(1,2);
compute.div(1,2);

1. 函数内的this指向

函数的 不同调用方式 决定了 this的指向不同

  1. 普通函数——this 指向window
  2. 对象的方法——this 指向的是对象
  3. 构造函数——this 指向实例对象
    (原型对象里面的this 也指向这个实例对象)
  4. 绑定事件函数——this 指向这个函数的调用者
  5. 定时器函数——this 指向的是window
  6. 立即执行函数——this 指向的是window
2. 改变函数内this指向:

call( )、 apply( )、bind( ) 三种方法

1. call方法
  1. 可以调用函数
  2. 可以改变函数的this指向
  • 主要应用:实现继承
fun.call(thisArg,arg1,arg2,...)
2. apply方法
  1. 可以调用函数
  2. 可以改变函数的this指向
  3. 注意:它的参数必须是数组(伪数组)
  • 主要应用:例如我们可以利用apply借助于数学内置对象求数组最大值
//apply  应用的意识
fun.apply(thisArg,[argsArray])
  • thisArg:在fun函数运行时指定的this值
  • argsArray:传递的值,必须包含在数组里面
  • 返回值就是函数的返回值,因为他就是调用函数 ???
3. bind方法
  1. 不会调用函数
  2. 可以改变函数内部this指向
  3. 返回的是 原函数改变this指向之后产生的新函数
  • 主要应用:如果有的函数我们不需要立即调用,但是又想改变这个函数内部的this指向,此时,使用bind
// bind() 绑定 捆绑的意思
fun.bind(thisArg,arg1,arg2,...);
  • thisArg:在fun函数运行时指定的this值
  • arg1,arg2:传递的其他参数
  • 返回由指定的this 值和初始化参数改造的原函数拷贝

作业:

年龄为多少岁姓名为XX买了一辆排量为XX的什么颜色的什么牌子的车 call apply

  	  function Cars(opt){
            this.brand = opt.brand;
            this.color = opt.color;
            this.displacement = opt.displacement;
        }
       function Customer(opt){
            Cars.call(this,opt);
            this.name = opt.name;
            this.age = opt.age;
            this.income = opt.income;
            this. resultInfo = function(){
                console.log(this.age + '的' + this.name + '购买了一辆' + this.color + '的' +  this.brand + '排量是' + this.displacement);
            }
        }

        var customer1 = new Customer({
            name:'lili',
            age:21,
            income:23425,
            brand:'奔驰',
            color:'pink',
            displacement:5

        })
        customer1.resultInfo();
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值