zoukankan      html  css  js  c++  java
  • 招银网络面试

    原链接:https://www.nowcoder.com/discuss/110687

    目录

    1面

    java线程的实现方式,用runable实现一下,写出来

    blockingqueue实现原理

    数据库优化了解那些

    dom解析了解那些

    设计模式简单说一下

    2面

    说一下实习的项目

    如果有一个很大的表,要删除里面95%的数据该怎么做

    日志文件怎么去存储,mysql的存储引擎了解多少

    hr面


    1面


    java线程的实现方式,用runable实现一下,写出来

    创建线程的第二种方式:实现runable接口
    /*步骤
    1.定义类实现Runable接口
    2.覆盖Runable接口中的run方法
       将线程要运行的代码存放在该run方法中
    3.通过Thread类建立线程对象
    4.将Runable接口的子类对象作为实际参数传递给Thread类的构造函数
      为什么要将Runable接口的子类对象传递给Thread的构造函数。
      因为,自定义的run方法所属的对象是Runable接口的子类对象
      所以要让线程去指定对象的Run方法,就必须明确该run方法所属对象
    5.调用Thread类的start方法开启线程并调用Runable接口子类的Run方法

    实现方式和继承方式有什么区别?(面试题经常考)
    实现方式好处:避免了单继承的局限性
    在定义线程时,建议使用实现方式 

    两种方式区别:
    继承Thread:线程代码存放Thread子类的run方法中
    实现Runable:线程代码存在接口的子类的run方法  
     

    */
    
    class Ticket implements Runnable //extends Thread
    {
        private static int tick=100;
        public void run()
        {
            while(true)
            {
                if(tick>0)
                {
                    System.out.println(Thread.currentThread().getName()+"sale:--"+tick--);
                }
            }
        }
    }
    
    class TicketDemo
    {
        public static void main(String args[])
        {
            Ticket t=new Ticket();
            Thread t1=new Thread(t);
            Thread t2=new Thread(t);
            Thread t3=new Thread(t);
            Thread t4=new Thread(t);
            
            t1.start();
            t2.start();
            t3.start();
            t4.start();
        }
    }


    blockingqueue实现原理

    1. 前言

    BlockingQueue即阻塞队列,它算是一种将ReentrantLock用得非常精彩的一种表现,依据它的基本原理,我们可以实现Web中的长连接聊天功能,当然其最常用的还是用于实现生产者与消费者模式,大致如下图所示:

    在Java中,BlockingQueue是一个接口,它的实现类有ArrayBlockingQueue、DelayQueue、 LinkedBlockingDeque、LinkedBlockingQueue、PriorityBlockingQueue、SynchronousQueue等,它们的区别主要体现在存储结构上或对元素操作上的不同,但是对于take与put操作的原理,却是类似的。下面的源码以ArrayBlockingQueue为例。

    2. 分析

    BlockingQueue内部有一个ReentrantLock,其生成了两个Condition,在ArrayBlockingQueue的属性声明中可以看见:

    /** Main lock guarding all access */
    final ReentrantLock lock;
    /** Condition for waiting takes */
    private final Condition notEmpty;
    /** Condition for waiting puts */
    private final Condition notFull;

    ...

    public ArrayBlockingQueue(int capacity, boolean fair) {
        if (capacity <= 0)
            throw new IllegalArgumentException();
        this.items = new Object[capacity];
        lock = new ReentrantLock(fair);
        notEmpty = lock.newCondition();
        notFull =  lock.newCondition();
    }
    而如果能把notEmpty、notFull、put线程、take线程拟人的话,那么我想put与take操作可能会是下面这种流程:

    put(e) 


    take() 


    其中ArrayBlockingQueue.put(E e)源码如下(其中中文注释为自定义注释,下同):

    /**
     * Inserts the specified element at the tail of this queue, waiting
     * for space to become available if the queue is full.
     *
     * @throws InterruptedException {@inheritDoc}
     * @throws NullPointerException {@inheritDoc}
     */
    public void put(E e) throws InterruptedException {
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == items.length)
                notFull.await(); // 如果队列已满,则等待
            insert(e);
        } finally {
            lock.unlock();
        }
    }

    /**
     * Inserts element at current put position, advances, and signals.
     * Call only when holding lock.
     */
    private void insert(E x) {
        items[putIndex] = x;
        putIndex = inc(putIndex);
        ++count;
        notEmpty.signal(); // 有新的元素被插入,通知等待中的取走元素线程
    }

    ArrayBlockingQueue.take()源码如下:

    public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == 0)
                notEmpty.await(); // 如果队列为空,则等待
            return extract();
        } finally {
            lock.unlock();
        }
    }

    /**
     * Extracts element at current take position, advances, and signals.
     * Call only when holding lock.
     */
    private E extract() {
        final Object[] items = this.items;
        E x = this.<E>cast(items[takeIndex]);
        items[takeIndex] = null;
        takeIndex = inc(takeIndex);
        --count;
        notFull.signal(); // 有新的元素被取走,通知等待中的插入元素线程
        return x;
    }

    可以看见,put(E)与take()是同步的,在put操作中,当队列满了,会阻塞put操作,直到队列中有空闲的位置。而在take操作中,当队列为空时,会阻塞take操作,直到队列中有新的元素。

    而这里使用两个Condition,则可以避免调用signal()时,会唤醒相同的put或take操作。

    数据库优化了解那些

    为了做知识的梳理的总结,在各位大神前辈的基础上来归纳总结一下数据库的知识和性能优化的方法。

    这部分在现实和面试中会经常遇到,在此做一个整理,请多多批评指正。

    一,面试求职:数据库常见面试题(数据库优化思路)

    这篇强推,作为数据库要点复习和归纳很好!比如:

    drop,delete与truncate的区别
    drop直接删掉表 truncate删除表中数据,再插入时自增长id又从1开始 delete删除表中数据,可以加where字句。
    (1) DELETE语句执行删除的过程是每次从表中删除一行,并且同时将该行的删除操作作为事务记录在日志中保存以便进行进行回滚操作。TRUNCATE TABLE 则一次性地从表中删除所有的数据并不把单独的删除操作记录记入日志保存,删除行是不能恢复的。并且在删除的过程中不会激活与表有关的删除触发器。执行速度快。

    (2) 表和索引所占空间。当表被TRUNCATE 后,这个表和索引所占用的空间会恢复到初始大小,而DELETE操作不会减少表或索引所占用的空间。drop语句将表所占用的空间全释放掉。

    (3) 一般而言,drop > truncate > delete

    (4) 应用范围。TRUNCATE 只能对TABLE;DELETE可以是table和view

    (5) TRUNCATE 和DELETE只删除数据,而DROP则删除整个表(结构和数据)。

    (6) truncate与不带where的delete :只删除数据,而不删除表的结构(定义)drop语句将删除表的结构被依赖的约束(constrain),触发器(trigger)索引(index);依赖于该表的存储过程/函数将被保留,但其状态会变为:invalid。

    (7) delete语句为DML(data maintain Language),这个操作会被放到 rollback segment中,事务提交后才生效。如果有相应的 tigger,执行的时候将被触发。

    (8) truncate、drop是DLL(data define language),操作立即生效,原数据不放到 rollback segment中,不能回滚

    (9) 在没有备份情况下,谨慎使用 drop 与 truncate。要删除部分数据行采用delete且注意结合where来约束影响范围。回滚段要足够大。要删除表用drop;若想保留表而将表中数据删除,如果于事务无关,用truncate即可实现。如果和事务有关,或老师想触发trigger,还是用delete。

    (10) Truncate table 表名 速度快,而且效率高,因为:
    truncate table 在功能上与不带 WHERE 子句的 DELETE 语句相同:二者均删除表中的全部行。但 TRUNCATE TABLE 比 DELETE 速度快,且使用的系统和事务日志资源少。DELETE 语句每次删除一行,并在事务日志中为所删除的每行记录一项。TRUNCATE TABLE 通过释放存储表数据所用的数据页来删除数据,并且只在事务日志中记录页的释放。

    (11) TRUNCATE TABLE 删除表中的所有行,但表结构及其列、约束、索引等保持不变。新行标识所用的计数值重置为该列的种子。如果想保留标识计数值,请改用 DELETE。如果要删除表定义及其数据,请使用 DROP TABLE 语句。

    (12) 对于由 FOREIGN KEY 约束引用的表,不能使用 TRUNCATE TABLE,而应使用不带 WHERE 子句的 DELETE 语句。由于 TRUNCATE TABLE 不记录在日志中,所以它不能激活触发器。

    表的连接

    一、外连接
    1.概念:包括左向外联接、右向外联接或完整外部联接

    2.左连接:left join 或 left outer join
    (1)左向外联接的结果集包括 LEFT OUTER 子句中指定的左表的所有行,而不仅仅是联接列所匹配的行。如果左表的某行在右表中没有匹配行,则在相关联的结果集行中右表的所有选择列表列均为空值(null)。
    (2)sql 语句
    select * from table1 left join table2 on table1.id=table2.id
    -------------结果-------------
    idnameidscore
    ------------------------------
    1lee190
    2zhang2100
    4wangNULLNULL
    ------------------------------
    注释:包含table1的所有子句,根据指定条件返回table2相应的字段,不符合的以null显示

    3.右连接:right join 或 right outer join
    (1)右向外联接是左向外联接的反向联接。将返回右表的所有行。如果右表的某行在左表中没有匹配行,则将为左表返回空值。
    (2)sql 语句
    select * from table1 right join table2 on table1.id=table2.id
    -------------结果-------------
    idnameidscore
    ------------------------------
    1lee190
    2zhang2100
    NULLNULL370
    ------------------------------
    注释:包含table2的所有子句,根据指定条件返回table1相应的字段,不符合的以null显示

    4.完整外部联接:full join 或 full outer join
    (1)完整外部联接返回左表和右表中的所有行。当某行在另一个表中没有匹配行时,则另一个表的选择列表列包含空值。如果表之间有匹配行,则整个结果集行包含基表的数据值。
    (2)sql 语句
    select * from table1 full join table2 on table1.id=table2.id
    -------------结果-------------
    idnameidscore
    ------------------------------
    1lee190
    2zhang2100
    4wangNULLNULL
    NULLNULL370
    ------------------------------
    注释:返回左右连接的和(见上左、右连接)

    二、内连接
    1.概念:内联接是用比较运算符比较要联接列的值的联接

    2.内连接:join 或 inner join

    3.sql 语句
    select * from table1 join table2 on table1.id=table2.id
    -------------结果-------------
    idnameidscore
    ------------------------------
    1lee190
    2zhang2100
    ------------------------------
    注释:只返回符合条件的table1和table2的列

    4.等价(与下列执行效果相同)
    A:select a.*,b.* from table1 a,table2 b where a.id=b.id
    B:select * from table1 cross join table2 where table1.id=table2.id (注:cross join后加条件只能用where,不能用on)

    三、交叉连接(完全)

    1.概念:没有 WHERE 子句的交叉联接将产生联接所涉及的表的笛卡尔积。第一个表的行数乘以第二个表的行数等于笛卡尔积结果集的大小。(table1和table2交叉连接产生3*3=9条记录)

    2.交叉连接:cross join (不带条件where...)

    3.sql语句
    select * from table1 cross join table2
    -------------结果-------------
    idnameidscore
    ------------------------------
    1lee190
    2zhang190
    4wang190
    1lee2100
    2zhang2100
    4wang2100
    1lee370
    2zhang370
    4wang370
    ------------------------------
    注释:返回3*3=9条记录,即笛卡尔积

    4.等价(与下列执行效果相同)
    A:select * from table1,table2

    数据库优化的思路
    这个我借鉴了慕课上关于数据库优化的课程。
    1.SQL语句优化
    1)应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。
    2)应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:
    select id from t where num is null
    可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:
    select id from t where num=0
    3)很多时候用 exists 代替 in 是一个好的选择
    4)用Where子句替换HAVING 子句 因为HAVING 只会在检索出所有记录之后才对结果集进行过滤
    2.索引优化
    看上文索引
    3.数据库结构优化
    1)范式优化: 比如消除冗余(节省空间。。) 2)反范式优化:比如适当加冗余等(减少join) 3)拆分表: 分区将数据在物理上分隔开,不同分区的数据可以制定保存在处于不同磁盘上的数据文件里。这样,当对这个表进行查询时,只需要在表分区中进行扫描,而不必进行全表扫描,明显缩短了查询时间,另外处于不同磁盘的分区也将对这个表的数据传输分散在不同的磁盘I/O,一个精心设置的分区可以将数据传输对磁盘I/O竞争均匀地分散开。对数据量大的时时表可采取此方法。可按月自动建表分区。
    4)拆分其实又分垂直拆分和水平拆分: 案例: 简单购物系统暂设涉及如下表: 1.产品表(数据量10w,稳定) 2.订单表(数据量200w,且有增长趋势) 3.用户表 (数据量100w,且有增长趋势) 以mysql为例讲述下水平拆分和垂直拆分,mysql能容忍的数量级在百万静态数据可以到千万 垂直拆分: 解决问题:表与表之间的io竞争 不解决问题:单表中数据量增长出现的压力 方案: 把产品表和用户表放到一个server上 订单表单独放到一个server上 水平拆分: 解决问题:单表中数据量增长出现的压力 不解决问题:表与表之间的io争夺
    方案: 用户表通过性别拆分为男用户表和女用户表 订单表通过已完成和完成中拆分为已完成订单和未完成订单 产品表 未完成订单放一个server上 已完成订单表盒男用户表放一个server上 女用户表放一个server上(女的爱购物 哈哈)
    4.服务器硬件优化
    这个么多花钱咯!

    二,MySQL数据库优化的八种方式(经典必看)https://www.cnblogs.com/zhyunfe/p/6209074.html

    1、选取最适用的字段属性,另外一个提高效率的方法是在可能的情况下,应该尽量把字段设置为NOTNULL

    2、使用连接(JOIN)来代替子查询(Sub-Queries)

    3、使用联合(UNION)来代替手动创建的临时表

    4、使用事务

    它的作用是:要么语句块中每条语句都操作成功,要么都失败。换句话说,就是可以保持数据库中数据的一致性和完整性。事物以BEGIN关键字开始,COMMIT关键字结束。在这之间的一条SQL操作失败,那么,ROLLBACK命令就可以把数据库恢复到BEGIN开始之前的状态。
    5、锁定表

    指锁定一个表,避免事务对整个数据库的锁定

    6、使用外键,锁定表的方法可以维护数据的完整性,但是它却不能保证数据的关联性。这个时候我们就可以使用外键。

    7、使用索引,索引是提高数据库性能的常用方法

    一般说来,索引应建立在那些将用于JOIN,WHERE判断和ORDERBY排序的字段上。尽量不要对数据库中某个含有大量重复的值的字段建立索引
    8、优化的查询语句

    最好是在相同类型的字段间进行比较的操作。

    在建有索引的字段上尽量不要使用函数进行操作。

    在搜索字符型字段时,我们有时会使用LIKE关键字和通配符,这种做法虽然简单,但却也是以牺牲系统性能为代价的。

    二、常见数据库优化方法 https://www.zhihu.com/question/36431635

    性能优化

    表的设计合理化,符合三大范式(3NF)
    1NF是对属性的原子性约束,要求属性(列)具有原子性,不可再分解;(只要是关系型数据库都满足1NF)
    2NF是对记录的惟一性约束,要求记录有惟一标识,即实体的惟一性;
    3NF是对字段冗余性的约束,它要求字段没有冗余。 没有冗余的数据库设计可以做到。
    添加适当索引(index) [四种: 普通索引、主键索引、唯一索引unique、全文索引]
    较频繁的作为查询条件字段应该创建索引;
    唯一性太差的字段不适合单独创建索引,即使频繁作为查询条件;
    更新非常频繁的字段不适合创建索引
    不会出现在WHERE子句中的字段不该创建索引
    分表技术(水平分割、垂直分割);
    读写[写: update/delete/add]分离;
    存储过程 [模块化编程,可以提高速度];
    对mysql配置优化 [配置最大并发数my.ini, 调整缓存大小 ];
    mysql服务器硬件升级;
    定时的去清除不需要的数据,定时进行碎片整理(MyISAM)。
    SQL语句优化

    通过show status命令了解各种SQL的执行频率;
    定位执行效率较低的SQL语句-(重点select;
    通过explain分析低效率的SQL;
    确定问题并采取相应的优化措施。
    添加索引

    索引主要可以分为以下几种:
    主键索引,主键自动的为主索引 (类型Primary);
    唯一索引 (UNIQUE);
    普通索引 (INDEX);
    全文索引 (FULLTEXT) [适用于MyISAM] ——》sphinx + 中文分词 coreseek [sphinx 的中文版 ];
    综合使用=>复合索引
    可能使用到索引
    对于创建的多列索引,只要查询条件使用了最左边的列,索引一般就会被使用。
    对于使用like的查询,查询如果是 ‘%aaa’ 不会使用到索引, ‘aaa%’ 会使用到索引。


    不使用索引
    如果条件中有or,即使其中有条件带索引也不会使用。
    对于多列索引,不是使用的第一部分,则不会使用索引。
    like查询是以%开头
    如果列类型是字符串,那一定要在条件中将数据使用引号引用起来。否则不使用索引。(添加时,字符串必须’’)
    如果mysql估计使用全表扫描要比使用索引快,则不使用索引。

    dom解析了解那些

    DOM是所有前端开发每天打交道的东西,但是随着jQuery等库的出现,大大简化了DOM操作,导致大家慢慢的“遗忘”了它的本来面貌。不过,要

    想深入学习前端知识,对DOM的了解是不可或缺的,所以本文力图系统的讲解下DOM的相关知识,如有遗漏或错误,还请大家指出一起讨论^ ^。

    一、DOM是什么?

    DOM(文档对象模型)是针对HTML和XML文档的一个API,通过DOM可以去改变文档。

    这个说法很官方,大家肯定还是不明白。

    举个例子:我们有一段HTML,那么如何访问第二层第一个节点呢,如何把最后一个节点移动到第一个节点上面去呢?

    DOM就是定义了如果做类似操作,那么应该怎么做的标准。比如用getElementById来访问节点,用insertBefore来插入节点。

    当浏览器载入HTML时,会生成相应的DOM树。

    简而言之,DOM可以理解为一个访问或操作HTML各种标签的实现标准。

    对于一个HTML来说,文档节点Document(看不到的)是它的根节点,对应的对象便是document对象(严格讲是子类HTMLDocument对象,下面单独介绍Document类型时会指出)。

    换句话说存在一个文档节点Document,然后它有子节点,比如通过document.getElementsByTagName(“html”),得到类型为元素节点的Element html。

    每一段HTML标记都可以用相应的节点表示,例如:

    HTML元素通过元素节点表示,注释通过注释节点表示,文档类型通过文档类型节点表示等。

    一共定义了12种节点类型,而这些类型又都继承自Node类型。

    所以我们首先讲Node类型,因为这个类型的方法是所有节点都会继承的。

    二、Node类型(基类,所有节点都继承了它的方法)

    Node是所有节点的基类型,所有节点都继承自它,所以所有节点都有一些共同的方法和属性。

    先讲Node类型的属性

    首先是nodeType属性,用来表明节点类型的,例如:

    document.nodeType;    // 返回 9 ,其中document对象为文档节点Document的实例

    这里面,9代表的就是DOCUMENT_NODE节点的意思,可以通过Node.DOCUMENT_NODE查看节点对应的数字

    document.nodeType === Node.DOCUMENT_NODE;    // true

    至于一共有哪些节点,每个节点对应的数字又是多少,这个可以问谷歌就知道了。反正常用的就是元素节点Element(对应数字为1)和文本节点Text(对应数字为3)

    然后常用的还有nodeName和nodeValue

    对于元素节点 nodeName就是标签名,nodeValue就是null

    对于文本节点 nodeName为”#text”(chrome里面测试的),nodeValue就是实际的值

    每个节点还有childNodes属性,这是个十分重要的属性,它保存了这个节点所有直接子元素

    调用childNodes返回的是一个NodeList对象,它极其像数组,但是有一个最关键的地方,它是动态查询的,也就是说每次调用它都会对DOM结构查询,所以对它的使用需要慎重,注意性能。

    访问childNodes可以使用数组下表或者item方法

    然后各个节点还存在各种属性让它们可以相互访问,下图很好的总结了

    比较有用的方法和属性:

    还有一点,如果要动态写入脚本 例如 xxx这样的

    ,那么要注意把分开来拼装下,否则会被误以为是脚本结束的标志,导致这个结束符匹配到上面一个开始符。可以这样

    写””;

    四、Element类型

    接下来讲讲最重要也是最常见的一个类型,Element类型。

    我们日常所操作的都是Element类型(实质是HTMLElement,这里为了方便理解,就简单这么说),比如

    document.getElementById("test")

    返回的就是Element类型。我们日常所说的“DOM对象”,通常也就是指Element类型的对象。

    然后说说这个类型的常见属性:

    首先最开始说的Node类型上的那些属性方法它都有,这个就不再重复了,主要说说它自己独有的。

    首先是tagName,这个和继承自Node类型的nodeName一样。都是返回标签名,通常是大写,结果取决于浏览器。所以在做比较

    的时候最好是调用下类似toLowerCase()这种方法再做比较。

    说说上面提到过的HTMLElement类型

    HTMLElement类型继承自Element类型,也是HTML元素的实际类型,我们在浏览器里用的元素都是这个类型。

    这个类型都具有一些标准属性,比如:

    id 元素的唯一标识

    title 通常是鼠标移上去时候会显示的信息

    className 类名

    等等,这几个属性是可读写的,也就是说你改变他们会得到相应的效果。

    除了属性外,还有几个重要的方法

    首先说说操作节点属性的方法

    getAttribute 、setAttribute 、removeAttribute这3个方法。

    这些是操作属性最常用的方法了,怎么用就不说了,很简单,顾名思义。

    还有一个attributes属性,保存了元素的全部属性。

    这里停下来,出个问题,ele.className 和 ele.getAttribute(“class”)返回的结果是不是同一个东西?

    解答这个问题,我要说一个重要知识点,一个元素的属性结构是这么来的,比如一个inpnt元素

    那么这个元素的属性被包含在 input.attributes里面,比如你在html元素上看到的class、id或者你自己定义的data-test这种属性。

    然后 getAttribute 、setAttribute 、removeAttribute这3个方法可以认为是快捷的取attributes集合的方法。而直接input.id或者input.className都是直接挂在input下的属性,和attributes是同级的。所以返回的东西也许看过去一样,实际是不一样的,不信你可以试试input.checked这input.getAttribute(“checked”)试试。

    关于这个知识点,详细的说可以再写一篇文章,在我的博客从is(“:checked”)说起中有谈到过,大家可以看看这篇文章和文章后的讨论,便可以知道是怎么一回事。

    总得来说,这3个方法通常用了处理自定义的属性,而不是id、class等这种“公认特性”。

    接下来说说创建元素

    document.createElement()可以创建一个元素,比如:

    document.createElement("div");

    一般之后可以为元素设置属性,两种方法,一种是直接node.property还可以node.setAttribute(“propertyName”,”value”)。等

    但是做完这些之后,这个元素还是没有在页面中,所以你还得通过最上面讲的类似appendChild这些方法把元素添加到页面里面。

    在IE中,还可以直接穿整个HTML字符串进去,来创建元素,比如

    document.createElement("

    test

    ");

    最后,元素节点也支持HTMLDocument类型的那些查找方法,比如getElementsByTagName。不过它只会找自己后代的节点。所以可以这么写代码

    document.getElementById("test").getElementsByTagName("div");    // 找到id为test元素下的所有div节点

    五、Text类型

    这个类型很特殊,也是第三常见类型(第一第二分别就是Document和Element)。

    这个节点简单来说就是一段字符串。

    有个很重要的特征就是,它没有子元素(不过这个仔细想想也知道= =)

    访问text节点的文本内容,可以通过nodeValue或者data属性。

    下面简单说说它提供的一些方法

    appendData();    // 在text末尾加内容

    deleteData(offset, count);    // 从offset指定的位置开始删除count个字符

    还有insertDate、replaceData、splitText等方法,就不一一说了,用的机会很少,可以用的时候再查阅。

    然后它还有一个lenght属性,返回字符长度的。

    这里说一个常见的坑。比如下面这个html结构

    这里,ul的第一个子节点(firstChild)是什么呢?第一眼看过去,肯定认为是li了,但是实际上,你会发现不是li,而是一个文本节点!

    这是因为浏览器认为ul和第一个li之间有空白字符,所以就有文本节点了。

    这里一个常见的问题就是遍历ul的childNodes的时候,遍历的元素一定要判断下nodeType是不是等于1(等于1就代表是元素节点),这样才能跳过这个坑。否则你也可以删除所有的空格和换行符。

    创建文本节点的方法是document.createTextNode

    然后接下来和操作Element类型一样,就是再插入到元素中,浏览器就可以看到了。

    六、其他的一些类型 Comment、DocumentType和DocumentFragment

    这些不常用的一句话带过把

    Comment是注释节点

    DocumentType就是doctype节点,通过docment.doctype来访问

    DocumentFragment这个节点是一个文档片段,偶尔会用到。

    比如一种常见的用法是,在一个ul中插入3个li。

    如果你循环插入3次,那么浏览器就要渲染3次,对性能有蛮大的影响。

    所以大家一般这么做

    var fragment = document.createDocumentFragment();

    然后循环把li,用appendChild插入到fragment里面

    最后在一次把fragment插入到ul里面。这样就会很快。

    七、DOM扩展

    进过上面讲的这么多节点类型,想必大家对DOM节点已经有了很深的了解,下面讲一讲DOM扩展的一些东西。

    浏览器为了方便开发者,扩展了一些DOM功能。

    因为是浏览器自己扩展的,所以使用前兼容性问题一定要注意

    判断“标准模式”和“混杂模式”通过 document.compatMode和新的document.documentMode

    上面不是说了一个文本节点作为第一子元素的坑吗,所以浏览器又实现了一个children属性,这个属性只包含元素节点。

    为了方便判断A节点是不是B节点的子节点,引入了contains方法,比如

    B.contains(A);    // true就代表是,false就代表不是

    这个方法有兼容性问题,使用前可以谷歌解决方法。

    针对访问元素,又提供了4个方法innerText/innerHTML/outerTEXT/outerHTML。

    通过这些方法,可以读和写元素。

    其中,*TEXT是返回文本内容 *HTML是返回html文本。

    而outer*则是代表是否包含元素本身。

    实际使用来看,在读内容的时候 inner*和outer*没有区别。

    在把内容写入元素的时候,就是是否包含元素本身的区别。

    重要的是,这几个方法有性能问题,比如在IE中,通过inner*删除的节点,其绑定的事件依然在内存中,就很容易消耗大量内存。

    还有一个技巧是,插入大量的html代码,用innerHTML是非常快的,建议使用。

    设计模式简单说一下

    转:https://blog.csdn.net/strawqqhat/article/details/88368917

    2面

    说一下实习的项目


    如果有一个很大的表,要删除里面95%的数据该怎么做


    日志文件怎么去存储,mysql的存储引擎了解多少
     


    hr面


    套路很深
    有没有女朋友
    看一下成绩单,有一科成绩比较差,就对着这科问
    最想去哪个城市
    自己最大的缺点是什么
    实习期间遇到的困难
    实习的公司领导怎么样,环境怎么样
    为什么不喜欢北京

  • 相关阅读:
    css水平垂直居中问题
    关系型数据库四大特性
    C++读取csv文件&&收获到的知识
    恒生面试记录
    SQL数据库操作命令
    安防产品知识记录
    学会求助(带着自己的理解去和别人探讨解决方案),处理问题责任清晰,如果不清楚可以问主管.
    一个简单又不简单的socket例子
    C++面试题总结
    大华电话面试
  • 原文地址:https://www.cnblogs.com/strawqqhat/p/10602236.html
Copyright © 2011-2022 走看看